Pull Request branch dev-clone to main #1
|
|
@ -16,17 +16,17 @@ import { respondents } from "../../drizzle/schema/respondents";
|
||||||
import { forbidden, notFound } from "../../errors/DashboardError";
|
import { forbidden, notFound } from "../../errors/DashboardError";
|
||||||
|
|
||||||
export const userFormSchema = z.object({
|
export const userFormSchema = z.object({
|
||||||
name: z.string().min(1, "Name is required").max(255),
|
name: z.string().min(1, "Nama wajib diisi").max(255),
|
||||||
username: z.string().min(1, "Username is required").max(255),
|
username: z.string().min(1, "Username wajib diisi").max(255),
|
||||||
email: z.string().min(1, "Email is required").max(255),
|
email: z.string().min(1, "Email wajib diisi").max(255),
|
||||||
password: z.string().min(6, "Password is required"),
|
password: z.string().min(6, "Password wajib diisi"),
|
||||||
companyName: z.string().min(1, "Company name is required").max(255),
|
companyName: z.string().min(1, "Nama Perusahaan wajib diisi").max(255),
|
||||||
position: z.string().min(1, "Position is required").max(255),
|
position: z.string().min(1, "Jabatan wajib diisi").max(255),
|
||||||
workExperience: z.string().min(1, "Work experience is required").max(255),
|
workExperience: z.string().min(1, "Pengalaman Kerja wajib diisi").max(255),
|
||||||
address: z.string().min(1, "Address is required"),
|
address: z.string().min(1, "Alamat wajib diisi"),
|
||||||
phoneNumber: z.string().min(1, "Phone number is required").max(13),
|
phoneNumber: z.string().min(1, "Nomor Telepon wajib diisi").max(13),
|
||||||
isEnabled: z.string().default("false"),
|
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({
|
export const userUpdateSchema = userFormSchema.extend({
|
||||||
|
|
@ -342,7 +342,7 @@ const usersRoute = new Hono<HonoEnv>()
|
||||||
.values({
|
.values({
|
||||||
companyName: userData.companyName,
|
companyName: userData.companyName,
|
||||||
position: userData.position,
|
position: userData.position,
|
||||||
workExperience: userData.workExperience,
|
workExperience: userData.workExperience + " Tahun",
|
||||||
address: userData.address,
|
address: userData.address,
|
||||||
phoneNumber: userData.phoneNumber,
|
phoneNumber: userData.phoneNumber,
|
||||||
userId: newUser.id,
|
userId: newUser.id,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,14 @@
|
||||||
import client from "@/honoClient";
|
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 { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { getRouteApi, useSearch } from "@tanstack/react-router";
|
import { getRouteApi, useSearch } from "@tanstack/react-router";
|
||||||
import { deleteUser } from "../queries/userQueries";
|
import { deleteUser } from "../queries/userQueries";
|
||||||
|
|
@ -60,40 +69,34 @@ export default function UserDeleteModal() {
|
||||||
const isModalOpen = Boolean(searchParams.delete && userQuery.data);
|
const isModalOpen = Boolean(searchParams.delete && userQuery.data);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<AlertDialog open={isModalOpen} onOpenChange={() => navigate({ search: {} })}>
|
||||||
opened={isModalOpen}
|
<AlertDialogContent>
|
||||||
onClose={() => navigate({ search: {} })}
|
<AlertDialogHeader>
|
||||||
title={`Konfirmasi Hapus`}
|
<AlertDialogTitle>Konfirmasi Hapus</AlertDialogTitle>
|
||||||
>
|
<AlertDialogDescription>
|
||||||
<Text size="sm">
|
Apakah Anda yakin ingin menghapus pengguna{" "}
|
||||||
Apakah Anda yakin ingin menghapus pengguna{" "}
|
<strong>{userQuery.data?.name}</strong>?
|
||||||
<Text span fw={700}>
|
<br />
|
||||||
{userQuery.data?.name}
|
Tindakan ini tidak dapat diubah.
|
||||||
</Text>
|
</AlertDialogDescription>
|
||||||
? Tindakan ini tidak dapat diubah.
|
</AlertDialogHeader>
|
||||||
</Text>
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel
|
||||||
{/* {errorMessage && <Alert color="red">{errorMessage}</Alert>} */}
|
onClick={() => navigate({ search: {} })}
|
||||||
{/* Buttons */}
|
disabled={mutation.isPending}
|
||||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
>
|
||||||
<Button
|
Batal
|
||||||
variant="outline"
|
</AlertDialogCancel>
|
||||||
onClick={() => navigate({ search: {} })}
|
<Button
|
||||||
disabled={mutation.isPending}
|
variant="default"
|
||||||
>
|
color="red"
|
||||||
Batal
|
onClick={() => mutation.mutate({ id: userId })}
|
||||||
</Button>
|
disabled={mutation.isPending}
|
||||||
<Button
|
>
|
||||||
variant="subtle"
|
{mutation.isPending ? "Hapus..." : "Hapus Pengguna"}
|
||||||
// leftSection={<TbDeviceFloppy size={20} />}
|
</Button>
|
||||||
type="submit"
|
</AlertDialogFooter>
|
||||||
color="red"
|
</AlertDialogContent>
|
||||||
loading={mutation.isPending}
|
</AlertDialog>
|
||||||
onClick={() => mutation.mutate({ id: userId })}
|
|
||||||
>
|
|
||||||
Hapus Pengguna
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
</Modal>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
|
import client from "../../../honoClient";
|
||||||
import stringToColorHex from "@/utils/stringToColorHex";
|
import stringToColorHex from "@/utils/stringToColorHex";
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Button,
|
AvatarFallback,
|
||||||
Center,
|
AvatarImage
|
||||||
Flex,
|
} from "@/shadcn/components/ui/avatar";
|
||||||
Modal,
|
import { Modal, ScrollArea } from "@mantine/core";
|
||||||
ScrollArea,
|
import { Button } from "@/shadcn/components/ui/button";
|
||||||
Stack,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import { useForm } from "@mantine/form";
|
import { useForm } from "@mantine/form";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { getRouteApi } from "@tanstack/react-router";
|
import { getRouteApi } from "@tanstack/react-router";
|
||||||
import { createUser, updateUser } from "../queries/userQueries";
|
import { createUser, updateUser } from "../queries/userQueries";
|
||||||
import { TbDeviceFloppy } from "react-icons/tb";
|
|
||||||
import client from "../../../honoClient";
|
|
||||||
import { getUserByIdQueryOptions } from "../queries/userQueries";
|
import { getUserByIdQueryOptions } from "../queries/userQueries";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { notifications } from "@mantine/notifications";
|
import { notifications } from "@mantine/notifications";
|
||||||
|
|
@ -191,114 +188,113 @@ export default function UserFormModal() {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
opened={isModalOpen}
|
opened={isModalOpen}
|
||||||
onClose={() => navigate({ search: {} })}
|
onClose={() => navigate({ search: {} })}
|
||||||
title={modalTitle} //Uppercase first letter
|
title={modalTitle} //Uppercase first letter
|
||||||
scrollAreaComponent={ScrollArea.Autosize}
|
scrollAreaComponent={ScrollArea.Autosize}
|
||||||
size="md"
|
size="md"
|
||||||
>
|
>
|
||||||
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
<form onSubmit={form.onSubmit((values) => handleSubmit(values))}>
|
||||||
<Stack mt="sm" gap="lg" px="lg">
|
<div className="flex items-center justify-center my-2">
|
||||||
{/* Avatar */}
|
<div className="h-120 w-120 rounded-full overflow-hidden">
|
||||||
<Center>
|
<Avatar className="h-[120px] w-[120px] rounded-full overflow-hidden">
|
||||||
<Avatar
|
<AvatarImage src={form.values.photoProfileUrl} alt={form.values.name} />
|
||||||
color={stringToColorHex(form.values.id ?? "")}
|
<AvatarFallback style={{ backgroundColor: stringToColorHex(form.values.id ?? ""), fontSize: "60px", color: "white", fontWeight: "bold" }}>
|
||||||
src={form.values.photoProfileUrl}
|
{form.values.name?.[0]?.toUpperCase() ?? "?"}
|
||||||
size={120}
|
</AvatarFallback>
|
||||||
>
|
</Avatar>
|
||||||
{form.values.name?.[0]?.toUpperCase()}
|
</div>
|
||||||
</Avatar>
|
</div>
|
||||||
</Center>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{createInputComponents({
|
<ScrollArea className="h-72 pr-4">
|
||||||
disableAll: mutation.isPending,
|
{createInputComponents({
|
||||||
readonlyAll: formType === "detail",
|
disableAll: mutation.isPending,
|
||||||
inputs: [
|
readonlyAll: formType === "detail",
|
||||||
{
|
inputs: [
|
||||||
type: "text",
|
{
|
||||||
label: "Nama",
|
type: "text",
|
||||||
...form.getInputProps("name"),
|
label: "Nama",
|
||||||
},
|
...form.getInputProps("name"),
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Jabatan",
|
type: "text",
|
||||||
...form.getInputProps("position"),
|
label: "Jabatan",
|
||||||
},
|
...form.getInputProps("position"),
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Pengalaman Kerja",
|
type: "text",
|
||||||
...form.getInputProps("workExperience"),
|
label: "Pengalaman Kerja",
|
||||||
},
|
...form.getInputProps("workExperience"),
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Email",
|
type: "text",
|
||||||
...form.getInputProps("email"),
|
label: "Email",
|
||||||
},
|
...form.getInputProps("email"),
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Instansi/Perusahaan",
|
type: "text",
|
||||||
...form.getInputProps("companyName"),
|
label: "Instansi/Perusahaan",
|
||||||
},
|
...form.getInputProps("companyName"),
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Alamat",
|
type: "text",
|
||||||
...form.getInputProps("address"),
|
label: "Alamat",
|
||||||
},
|
...form.getInputProps("address"),
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Nomor Telepon",
|
type: "text",
|
||||||
...form.getInputProps("phoneNumber"),
|
label: "Nomor Telepon",
|
||||||
},
|
...form.getInputProps("phoneNumber"),
|
||||||
|
},
|
||||||
{
|
|
||||||
type: "multi-select",
|
{
|
||||||
label: "Roles",
|
type: "multi-select",
|
||||||
value: form.values.roles,
|
label: "Roles",
|
||||||
onChange: (values) =>
|
value: form.values.roles,
|
||||||
form.setFieldValue("roles", values),
|
onChange: (values) =>
|
||||||
data: rolesQuery.data?.map((role) => ({
|
form.setFieldValue("roles", values),
|
||||||
value: role.id,
|
data: rolesQuery.data?.map((role) => ({
|
||||||
label: role.name,
|
value: role.id,
|
||||||
})),
|
label: role.name,
|
||||||
error: form.errors.roles,
|
})),
|
||||||
},
|
error: form.errors.roles,
|
||||||
{
|
},
|
||||||
type: "text",
|
{
|
||||||
label: "Username",
|
type: "text",
|
||||||
...form.getInputProps("username"),
|
label: "Username",
|
||||||
},
|
...form.getInputProps("username"),
|
||||||
{
|
},
|
||||||
type: "password",
|
{
|
||||||
label: "Password",
|
type: "password",
|
||||||
hidden: formType !== "tambah",
|
label: "Password",
|
||||||
...form.getInputProps("password"),
|
hidden: formType !== "tambah",
|
||||||
},
|
...form.getInputProps("password"),
|
||||||
],
|
},
|
||||||
})}
|
],
|
||||||
|
})}
|
||||||
|
</ScrollArea>
|
||||||
|
|
||||||
{/* Buttons */}
|
{/* Buttons */}
|
||||||
<Flex justify="flex-end" align="center" gap="lg" mt="lg">
|
<div className="flex justify-end align-center gap-1 mt-4">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => navigate({ search: {} })}
|
onClick={() => navigate({ search: {} })}
|
||||||
disabled={mutation.isPending}
|
disabled={mutation.isPending}
|
||||||
>
|
>
|
||||||
Tutup
|
Tutup
|
||||||
</Button>
|
</Button>
|
||||||
{formType !== "detail" && (
|
{formType !== "detail" && (
|
||||||
<Button
|
<Button
|
||||||
variant="filled"
|
variant="default"
|
||||||
leftSection={<TbDeviceFloppy size={20} />}
|
type="submit"
|
||||||
type="submit"
|
disabled={mutation.isPending}
|
||||||
loading={mutation.isPending}
|
>
|
||||||
>
|
Simpan
|
||||||
Simpan
|
</Button>
|
||||||
</Button>
|
)}
|
||||||
)}
|
</div>
|
||||||
</Flex>
|
</form>
|
||||||
</form>
|
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { createLazyFileRoute } from "@tanstack/react-router";
|
||||||
import UserFormModal from "@/modules/usersManagement/modals/UserFormModal";
|
import UserFormModal from "@/modules/usersManagement/modals/UserFormModal";
|
||||||
import ExtractQueryDataType from "@/types/ExtractQueryDataType";
|
import ExtractQueryDataType from "@/types/ExtractQueryDataType";
|
||||||
import { createColumnHelper } from "@tanstack/react-table";
|
import { createColumnHelper } from "@tanstack/react-table";
|
||||||
import { Badge, Flex } from "@mantine/core";
|
|
||||||
import createActionButtons from "@/utils/createActionButton";
|
import createActionButtons from "@/utils/createActionButton";
|
||||||
import { TbEye, TbPencil, TbTrash } from "react-icons/tb";
|
import { TbEye, TbPencil, TbTrash } from "react-icons/tb";
|
||||||
import UserDeleteModal from "@/modules/usersManagement/modals/UserDeleteModal";
|
import UserDeleteModal from "@/modules/usersManagement/modals/UserDeleteModal";
|
||||||
|
|
@ -61,7 +60,7 @@ export default function UsersPage() {
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
header: "Aksi",
|
header: "Aksi",
|
||||||
cell: (props) => (
|
cell: (props) => (
|
||||||
<Flex gap="xs">
|
<div className="flex gap-x-2">
|
||||||
{createActionButtons([
|
{createActionButtons([
|
||||||
{
|
{
|
||||||
label: "Detail",
|
label: "Detail",
|
||||||
|
|
@ -85,7 +84,7 @@ export default function UsersPage() {
|
||||||
icon: <TbTrash />,
|
icon: <TbTrash />,
|
||||||
},
|
},
|
||||||
])}
|
])}
|
||||||
</Flex>
|
</div>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user