Pull Request branch dev-clone to main #1

Merged
gitea merged 429 commits from dev-clone into main 2024-12-23 09:31:34 +00:00
4 changed files with 158 additions and 160 deletions
Showing only changes of commit d3da289363 - Show all commits

View File

@ -16,17 +16,17 @@ import { respondents } from "../../drizzle/schema/respondents";
import { forbidden, notFound } from "../../errors/DashboardError";
export const userFormSchema = z.object({
name: z.string().min(1, "Name is required").max(255),
username: z.string().min(1, "Username is required").max(255),
email: z.string().min(1, "Email is required").max(255),
password: z.string().min(6, "Password is required"),
companyName: z.string().min(1, "Company name is required").max(255),
position: z.string().min(1, "Position is required").max(255),
workExperience: z.string().min(1, "Work experience is required").max(255),
address: z.string().min(1, "Address is required"),
phoneNumber: z.string().min(1, "Phone number is required").max(13),
name: z.string().min(1, "Nama wajib diisi").max(255),
username: z.string().min(1, "Username wajib diisi").max(255),
email: z.string().min(1, "Email wajib diisi").max(255),
password: z.string().min(6, "Password wajib diisi"),
companyName: z.string().min(1, "Nama Perusahaan wajib diisi").max(255),
position: z.string().min(1, "Jabatan wajib diisi").max(255),
workExperience: z.string().min(1, "Pengalaman Kerja wajib diisi").max(255),
address: z.string().min(1, "Alamat wajib diisi"),
phoneNumber: z.string().min(1, "Nomor Telepon wajib diisi").max(13),
isEnabled: z.string().default("false"),
roles: z.array(z.string().min(1, "Role is required")),
roles: z.array(z.string().min(1, "Role wajib diisi")),
});
export const userUpdateSchema = userFormSchema.extend({
@ -342,7 +342,7 @@ const usersRoute = new Hono<HonoEnv>()
.values({
companyName: userData.companyName,
position: userData.position,
workExperience: userData.workExperience,
workExperience: userData.workExperience + " Tahun",
address: userData.address,
phoneNumber: userData.phoneNumber,
userId: newUser.id,

View File

@ -1,5 +1,14 @@
import client from "@/honoClient";
import { Button, Flex, Modal, Text } from "@mantine/core";
import {
AlertDialog,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from "@/shadcn/components/ui/alert-dialog";
import { Button } from "@/shadcn/components/ui/button";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { getRouteApi, useSearch } from "@tanstack/react-router";
import { deleteUser } from "../queries/userQueries";
@ -60,40 +69,34 @@ export default function UserDeleteModal() {
const isModalOpen = Boolean(searchParams.delete && userQuery.data);
return (
<Modal
opened={isModalOpen}
onClose={() => navigate({ search: {} })}
title={`Konfirmasi Hapus`}
>
<Text size="sm">
Apakah Anda yakin ingin menghapus pengguna{" "}
<Text span fw={700}>
{userQuery.data?.name}
</Text>
? Tindakan ini tidak dapat diubah.
</Text>
{/* {errorMessage && <Alert color="red">{errorMessage}</Alert>} */}
{/* Buttons */}
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
<Button
variant="outline"
onClick={() => navigate({ search: {} })}
disabled={mutation.isPending}
>
Batal
</Button>
<Button
variant="subtle"
// leftSection={<TbDeviceFloppy size={20} />}
type="submit"
color="red"
loading={mutation.isPending}
onClick={() => mutation.mutate({ id: userId })}
>
Hapus Pengguna
</Button>
</Flex>
</Modal>
<AlertDialog open={isModalOpen} onOpenChange={() => navigate({ search: {} })}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Konfirmasi Hapus</AlertDialogTitle>
<AlertDialogDescription>
Apakah Anda yakin ingin menghapus pengguna{" "}
<strong>{userQuery.data?.name}</strong>?
<br />
Tindakan ini tidak dapat diubah.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel
onClick={() => navigate({ search: {} })}
disabled={mutation.isPending}
>
Batal
</AlertDialogCancel>
<Button
variant="default"
color="red"
onClick={() => mutation.mutate({ id: userId })}
disabled={mutation.isPending}
>
{mutation.isPending ? "Hapus..." : "Hapus Pengguna"}
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
}

View File

@ -1,19 +1,16 @@
import client from "../../../honoClient";
import stringToColorHex from "@/utils/stringToColorHex";
import {
Avatar,
Button,
Center,
Flex,
Modal,
ScrollArea,
Stack,
} from "@mantine/core";
AvatarFallback,
AvatarImage
} from "@/shadcn/components/ui/avatar";
import { Modal, ScrollArea } from "@mantine/core";
import { Button } from "@/shadcn/components/ui/button";
import { useForm } from "@mantine/form";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { getRouteApi } from "@tanstack/react-router";
import { createUser, updateUser } from "../queries/userQueries";
import { TbDeviceFloppy } from "react-icons/tb";
import client from "../../../honoClient";
import { getUserByIdQueryOptions } from "../queries/userQueries";
import { useEffect } from "react";
import { notifications } from "@mantine/notifications";
@ -191,114 +188,113 @@ export default function UserFormModal() {
});
return (
<Modal
<Modal
opened={isModalOpen}
onClose={() => navigate({ search: {} })}
title={modalTitle} //Uppercase first letter
scrollAreaComponent={ScrollArea.Autosize}
size="md"
>
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
<Stack mt="sm" gap="lg" px="lg">
{/* Avatar */}
<Center>
<Avatar
color={stringToColorHex(form.values.id ?? "")}
src={form.values.photoProfileUrl}
size={120}
>
{form.values.name?.[0]?.toUpperCase()}
</Avatar>
</Center>
</Stack>
>
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
<div className="flex items-center justify-center my-2">
<div className="h-120 w-120 rounded-full overflow-hidden">
<Avatar className="h-[120px] w-[120px] rounded-full overflow-hidden">
<AvatarImage src={form.values.photoProfileUrl} alt={form.values.name} />
<AvatarFallback style={{ backgroundColor: stringToColorHex(form.values.id ?? ""), fontSize: "60px", color: "white", fontWeight: "bold" }}>
{form.values.name?.[0]?.toUpperCase() ?? "?"}
</AvatarFallback>
</Avatar>
</div>
</div>
{createInputComponents({
disableAll: mutation.isPending,
readonlyAll: formType === "detail",
inputs: [
{
type: "text",
label: "Nama",
...form.getInputProps("name"),
},
{
type: "text",
label: "Jabatan",
...form.getInputProps("position"),
},
{
type: "text",
label: "Pengalaman Kerja",
...form.getInputProps("workExperience"),
},
{
type: "text",
label: "Email",
...form.getInputProps("email"),
},
{
type: "text",
label: "Instansi/Perusahaan",
...form.getInputProps("companyName"),
},
{
type: "text",
label: "Alamat",
...form.getInputProps("address"),
},
{
type: "text",
label: "Nomor Telepon",
...form.getInputProps("phoneNumber"),
},
<ScrollArea className="h-72 pr-4">
{createInputComponents({
disableAll: mutation.isPending,
readonlyAll: formType === "detail",
inputs: [
{
type: "text",
label: "Nama",
...form.getInputProps("name"),
},
{
type: "text",
label: "Jabatan",
...form.getInputProps("position"),
},
{
type: "text",
label: "Pengalaman Kerja",
...form.getInputProps("workExperience"),
},
{
type: "text",
label: "Email",
...form.getInputProps("email"),
},
{
type: "text",
label: "Instansi/Perusahaan",
...form.getInputProps("companyName"),
},
{
type: "text",
label: "Alamat",
...form.getInputProps("address"),
},
{
type: "text",
label: "Nomor Telepon",
...form.getInputProps("phoneNumber"),
},
{
type: "multi-select",
label: "Roles",
value: form.values.roles,
onChange: (values) =>
form.setFieldValue("roles", values),
data: rolesQuery.data?.map((role) => ({
value: role.id,
label: role.name,
})),
error: form.errors.roles,
},
{
type: "text",
label: "Username",
...form.getInputProps("username"),
},
{
type: "password",
label: "Password",
hidden: formType !== "tambah",
...form.getInputProps("password"),
},
],
})}
{
type: "multi-select",
label: "Roles",
value: form.values.roles,
onChange: (values) =>
form.setFieldValue("roles", values),
data: rolesQuery.data?.map((role) => ({
value: role.id,
label: role.name,
})),
error: form.errors.roles,
},
{
type: "text",
label: "Username",
...form.getInputProps("username"),
},
{
type: "password",
label: "Password",
hidden: formType !== "tambah",
...form.getInputProps("password"),
},
],
})}
</ScrollArea>
{/* Buttons */}
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
<Button
variant="outline"
onClick={() => navigate({ search: {} })}
disabled={mutation.isPending}
>
Tutup
</Button>
{formType !== "detail" && (
<Button
variant="filled"
leftSection={<TbDeviceFloppy size={20} />}
type="submit"
loading={mutation.isPending}
>
Simpan
</Button>
)}
</Flex>
</form>
{/* Buttons */}
<div className="flex justify-end align-center gap-1 mt-4">
<Button
variant="outline"
onClick={() => navigate({ search: {} })}
disabled={mutation.isPending}
>
Tutup
</Button>
{formType !== "detail" && (
<Button
variant="default"
type="submit"
disabled={mutation.isPending}
>
Simpan
</Button>
)}
</div>
</form>
</Modal>
);
}

View File

@ -4,7 +4,6 @@ import { createLazyFileRoute } from "@tanstack/react-router";
import UserFormModal from "@/modules/usersManagement/modals/UserFormModal";
import ExtractQueryDataType from "@/types/ExtractQueryDataType";
import { createColumnHelper } from "@tanstack/react-table";
import { Badge, Flex } from "@mantine/core";
import createActionButtons from "@/utils/createActionButton";
import { TbEye, TbPencil, TbTrash } from "react-icons/tb";
import UserDeleteModal from "@/modules/usersManagement/modals/UserDeleteModal";
@ -61,7 +60,7 @@ export default function UsersPage() {
columnHelper.display({
header: "Aksi",
cell: (props) => (
<Flex gap="xs">
<div className="flex gap-x-2">
{createActionButtons([
{
label: "Detail",
@ -85,7 +84,7 @@ export default function UsersPage() {
icon: <TbTrash />,
},
])}
</Flex>
</div>
),
}),
]}