feat: simplify testing structure into api and e2e categories
This commit is contained in:
146
src/routes/profile/edit.tsx
Normal file
146
src/routes/profile/edit.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Container,
|
||||
Divider,
|
||||
Group,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Title,
|
||||
} from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { IconChevronLeft, IconEdit } from "@tabler/icons-react";
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import { useState } from "react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import { protectedRouteMiddleware } from "@/middleware/authMiddleware";
|
||||
import { apiClient } from "@/utils/api-client";
|
||||
import { authStore } from "../../store/auth";
|
||||
|
||||
export const Route = createFileRoute("/profile/edit")({
|
||||
component: EditProfile,
|
||||
beforeLoad: protectedRouteMiddleware,
|
||||
onEnter({ context }) {
|
||||
authStore.user = context?.user as any;
|
||||
authStore.session = context?.session as any;
|
||||
},
|
||||
});
|
||||
|
||||
function EditProfile() {
|
||||
const snap = useSnapshot(authStore);
|
||||
const navigate = useNavigate();
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
|
||||
const form = useForm({
|
||||
initialValues: {
|
||||
name: snap.user?.name || "",
|
||||
image: snap.user?.image || "",
|
||||
},
|
||||
validate: {
|
||||
name: (value) =>
|
||||
value.length < 2 ? "Name must have at least 2 letters" : null,
|
||||
},
|
||||
});
|
||||
|
||||
const handleUpdateProfile = async (values: typeof form.values) => {
|
||||
try {
|
||||
setIsUpdating(true);
|
||||
const { data, error } = await apiClient.POST("/api/profile/update", {
|
||||
body: values,
|
||||
});
|
||||
|
||||
if (data?.user) {
|
||||
authStore.user = {
|
||||
...authStore.user,
|
||||
...data.user,
|
||||
} as any;
|
||||
navigate({ to: "/profile" });
|
||||
} else if (error) {
|
||||
console.error("Update error:", error);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to update profile:", err);
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Container size="sm" py={50}>
|
||||
<Stack gap="xl">
|
||||
<Group justify="space-between" align="center">
|
||||
<Box>
|
||||
<Title order={1} c="#f3d5a3">
|
||||
Edit Profil
|
||||
</Title>
|
||||
<Text c="dimmed" size="sm">
|
||||
Perbarui informasi profil publik Anda
|
||||
</Text>
|
||||
</Box>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
leftSection={<IconChevronLeft size={18} />}
|
||||
onClick={() => navigate({ to: "/profile" })}
|
||||
>
|
||||
Kembali
|
||||
</Button>
|
||||
</Group>
|
||||
|
||||
<Divider color="rgba(251, 240, 223, 0.1)" />
|
||||
|
||||
<Card
|
||||
withBorder
|
||||
radius="md"
|
||||
p="xl"
|
||||
bg="rgba(26, 26, 26, 0.5)"
|
||||
style={{ border: "1px solid rgba(251, 240, 223, 0.1)" }}
|
||||
>
|
||||
<form onSubmit={form.onSubmit(handleUpdateProfile)}>
|
||||
<Stack gap="md">
|
||||
<TextInput
|
||||
label="Nama Lengkap"
|
||||
placeholder="Masukkan nama lengkap Anda"
|
||||
{...form.getInputProps("name")}
|
||||
styles={{
|
||||
label: { color: "#fbf0df", marginBottom: 8 },
|
||||
input: {
|
||||
backgroundColor: "rgba(0,0,0,0.2)",
|
||||
color: "#fbf0df",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="URL Foto Profil"
|
||||
placeholder="https://example.com/photo.jpg"
|
||||
{...form.getInputProps("image")}
|
||||
styles={{
|
||||
label: { color: "#fbf0df", marginBottom: 8 },
|
||||
input: {
|
||||
backgroundColor: "rgba(0,0,0,0.2)",
|
||||
color: "#fbf0df",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
fullWidth
|
||||
mt="lg"
|
||||
size="md"
|
||||
color="orange"
|
||||
loading={isUpdating}
|
||||
leftSection={<IconEdit size={18} />}
|
||||
>
|
||||
Simpan Perubahan
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</Card>
|
||||
</Stack>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
// Need Box from @mantine/core
|
||||
import { Box } from "@mantine/core";
|
||||
@@ -17,12 +17,14 @@ import {
|
||||
Title,
|
||||
Tooltip,
|
||||
} from "@mantine/core";
|
||||
|
||||
import { modals } from "@mantine/modals";
|
||||
import {
|
||||
IconAt,
|
||||
IconCheck,
|
||||
IconCopy,
|
||||
IconDashboard,
|
||||
IconEdit,
|
||||
IconExternalLink,
|
||||
IconId,
|
||||
IconLogout,
|
||||
@@ -34,9 +36,9 @@ import { useState } from "react";
|
||||
import { useSnapshot } from "valtio";
|
||||
import { protectedRouteMiddleware } from "@/middleware/authMiddleware";
|
||||
import { authClient } from "@/utils/auth-client";
|
||||
import { authStore } from "../store/auth";
|
||||
import { authStore } from "../../store/auth";
|
||||
|
||||
export const Route = createFileRoute("/profile")({
|
||||
export const Route = createFileRoute("/profile/")({
|
||||
component: Profile,
|
||||
beforeLoad: protectedRouteMiddleware,
|
||||
onEnter({ context }) {
|
||||
@@ -161,6 +163,14 @@ function Profile() {
|
||||
Admin Panel
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="light"
|
||||
color="blue"
|
||||
leftSection={<IconEdit size={18} />}
|
||||
onClick={() => navigate({ to: "/profile/edit" })}
|
||||
>
|
||||
Edit Profil
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="red"
|
||||
Reference in New Issue
Block a user