feat: implement box management module with API services, UI components, and dependency updates

This commit is contained in:
2026-07-22 13:00:35 +07:00
parent ded6f0eb0a
commit 5de7476d85
23 changed files with 3586 additions and 2806 deletions

View File

@@ -69,6 +69,8 @@
"ui_view_all_notifications": "View All Notifications",
"ui_label_notifications": "Notifications",
"ui_change_password_btn": "Change password",
"ui_edit_house_btn": "Edit house",
"ui_edit_box_btn": "Edit box",
"nav_label_management": "Management",
"nav_label_basic": "Basic",
"nav_label_kanri": "Administrator",
@@ -242,6 +244,7 @@
"boxes_page_ui_table_header_tags": "Tags",
"boxes_page_ui_table_header_user": "Creater",
"boxes_page_ui_table_header_house": "House",
"items_page_ui_title": "Vật phẩm",
"backend_message": [
{
"match": {

View File

@@ -72,6 +72,7 @@
"ui_label_notifications": "Thông báo",
"ui_change_password_btn": "Đổi mật khẩu",
"ui_edit_house_btn": "Chỉnh sửa nhà",
"ui_edit_box_btn": "Chỉnh sửa hộp chứa",
"nav_label_management": "Quản lý",
"nav_label_basic": "Cơ bản",
"nav_label_kanri": "Quản trị viên",
@@ -246,6 +247,7 @@
"boxes_page_ui_table_header_tags": "Nhãn dán",
"boxes_page_ui_table_header_user": "Người tạo",
"boxes_page_ui_table_header_house": "Thuộc nhà",
"items_page_ui_title": "Vật phẩm",
"backend_message": [
{
"match": {

View File

@@ -19,58 +19,57 @@
"db:seed": "dotenv -e .env.local -- prisma db seed"
},
"dependencies": {
"@base-ui/react": "^1.0.0",
"@base-ui/react": "^1.6.0",
"@fontsource-variable/inter": "^5.2.8",
"@phosphor-icons/react": "^2.1.10",
"@prisma/adapter-pg": "^7.1.0",
"@prisma/client": "^7.4.1",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/react-devtools": "^0.9.4",
"@tanstack/react-form": "^1.27.7",
"@tanstack/react-query": "^5.66.5",
"@tanstack/react-query-devtools": "^5.84.2",
"@tanstack/react-router": "^1.132.0",
"@tanstack/react-router-devtools": "^1.132.0",
"@tanstack/react-router-ssr-query": "^1.131.7",
"@tanstack/react-start": "^1.132.0",
"@prisma/adapter-pg": "^7.8.0",
"@prisma/client": "^7.8.0",
"@tailwindcss/vite": "^4.3.2",
"@tanstack/react-devtools": "^0.10.8",
"@tanstack/react-form": "^1.33.0",
"@tanstack/react-query": "^5.101.2",
"@tanstack/react-query-devtools": "^5.101.2",
"@tanstack/react-router": "^1.170.17",
"@tanstack/react-router-devtools": "^1.167.0",
"@tanstack/react-router-ssr-query": "^1.167.1",
"@tanstack/react-start": "^1.168.27",
"@tanstack/react-table": "^8.21.3",
"@tanstack/router-plugin": "^1.132.0",
"better-auth": "^1.4.10",
"@tanstack/router-plugin": "^1.168.19",
"better-auth": "^1.6.23",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"html-react-parser": "^5.2.16",
"html-react-parser": "^6.1.3",
"next-themes": "^0.4.6",
"pnpm": "^10.30.2",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"shadcn": "^3.6.1",
"pnpm": "^11.9.0",
"radix-ui": "^1.6.1",
"react": "^19.2.7",
"react-dom": "^19.2.7",
"shadcn": "^4.12.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.6",
"tw-animate-css": "^1.3.6",
"vite-tsconfig-paths": "^6.0.5",
"zod": "^4.3.6",
"zustand": "^5.0.11"
"tailwind-merge": "^3.6.0",
"tailwindcss": "^4.3.2",
"tw-animate-css": "^1.4.0",
"zod": "^4.4.3",
"zustand": "^5.0.14"
},
"devDependencies": {
"@inlang/paraglide-js": "2.10.0",
"@tanstack/devtools-vite": "^0.5.0",
"@tanstack/eslint-config": "^0.3.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/react": "^16.2.0",
"@types/node": "^25.2.0",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"@vitejs/plugin-react": "^5.0.4",
"@inlang/paraglide-js": "2.20.2",
"@tanstack/devtools-vite": "^0.8.1",
"@tanstack/eslint-config": "^0.4.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@types/node": "^26.1.0",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3",
"dotenv-cli": "^11.0.0",
"jsdom": "^28.0.0",
"prettier": "^3.8.1",
"prisma": "^7.4.1",
"tsx": "^4.20.6",
"typescript": "^5.7.2",
"vite": "^7.1.7",
"vitest": "^4.0.18",
"web-vitals": "^5.1.0"
"jsdom": "^29.1.1",
"prettier": "^3.9.4",
"prisma": "^7.8.0",
"tsx": "^4.22.5",
"typescript": "^6.0.3",
"vite": "^8.1.3",
"vitest": "^4.1.9",
"web-vitals": "^5.3.0"
}
}

5968
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -61,13 +61,18 @@ const BoxDisplay = <TData, TValue>({
key={row.id}
>
<div className="py-4 px-5 [&_svg]:w-full [&_svg]:h-full">
<BoxIcon />
<BoxIcon
color={(row.original as BoxWithCount).color || '#F09C14'}
/>
</div>
<div className="flex flex-col">
{row.getVisibleCells().map((cell) => {
if (cell.column.columnDef.id === 'actions') {
return (
<div className="bg-gray-200 py-1 px-3 justify-center">
<div
className="bg-gray-200 py-1 px-3 justify-center"
key={cell.id}
>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
@@ -78,6 +83,7 @@ const BoxDisplay = <TData, TValue>({
return (
<div
data-slot={cell.column.columnDef.id}
key={cell.id}
className={cn('px-5 mb-2 text-center w-full', {
'text-xs flex items-center justify-center gap-2':
cell.column.columnDef.id !== 'name',
@@ -89,16 +95,10 @@ const BoxDisplay = <TData, TValue>({
<TagIcon size={15} className="inline-block mr-2" />
)}
{cell.column.columnDef.id === 'quantity' && (
<CirclesThreeIcon
size={15}
className="inline-block mr-2"
/>
<CirclesThreeIcon size={15} />
)}
{cell.column.columnDef.id === 'time' && (
<HourglassIcon
size={15}
className="inline-block mr-2"
/>
<HourglassIcon size={15} />
)}
{flexRender(
cell.column.columnDef.cell,

View File

@@ -1,6 +1,7 @@
import { m } from '@/paraglide/messages';
import { formatters } from '@/utils/formatters';
import { ColumnDef } from '@tanstack/react-table';
import EditBoxDialog from './edit-box-dialog';
import ViewDetailBoxAction from './view-detail-dialog';
export const boxColumns: ColumnDef<BoxWithCount>[] = [
@@ -40,8 +41,9 @@ export const boxColumns: ColumnDef<BoxWithCount>[] = [
thClass: 'w-1/6',
},
cell: ({ row }) => (
<div className="flex justify-end w-full">
<div className="flex justify-center w-full">
<ViewDetailBoxAction data={row.original} />
<EditBoxDialog data={row.original} />
</div>
),
},

View File

@@ -1,46 +1,46 @@
const BoxIcon = () => {
const BoxIcon = ({ color = '#F09C14' }: { color?: string }) => {
return (
<svg viewBox="0 0 101 108" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="box">
<path
d="M50.7094 106.914L98.92 79.0797L98.9199 30.5769L56.8966 54.8177L50.7095 58.3867L50.7093 58.3866L50.7094 106.914Z"
fill="#F09C14"
fill={color}
/>
<path
d="M2.49883 79.0797L50.7094 106.914L50.7093 58.3866L44.5221 54.8176L2.49873 30.5767L2.49883 79.0797Z"
fill="#F09C14"
fill={color}
/>
<path
d="M50.7096 106.914L98.92 79.0797L98.9201 30.577L56.8966 54.8177L50.7097 58.3868L50.7095 58.3867L50.7096 106.914Z"
fill="black"
fill-opacity="0.1"
fillOpacity="0.1"
/>
<path
d="M50.7093 2.7423L2.49873 30.5767L44.5221 54.8176L50.7093 51.2454V2.7423Z"
fill="#F09C14"
fill={color}
/>
<path
d="M50.7093 58.3866L50.7095 58.3867L56.8966 54.8177L50.7093 51.2454L44.5221 54.8176L50.7093 58.3866Z"
fill="#F09C14"
fill={color}
/>
<path
d="M50.7093 2.7423V51.2454L56.8966 54.8177L98.9199 30.5769L50.7093 2.7423Z"
fill="#F09C14"
fill={color}
/>
<path
d="M50.7093 2.7423V51.2454L56.8966 54.8177L98.9199 30.5769L50.7093 2.7423Z"
fill="black"
fill-opacity="0.2"
fillOpacity="0.2"
/>
<path
d="M50.7091 58.3865L50.7093 58.3866L56.8964 54.8176L50.7091 51.2453L44.5219 54.8175L50.7091 58.3865Z"
fill="black"
fill-opacity="0.3"
fillOpacity="0.3"
/>
<path
d="M50.7091 2.74219L2.49854 30.5765L44.5219 54.8175L50.7091 51.2453V2.74219Z"
fill="black"
fill-opacity="0.3"
fillOpacity="0.3"
/>
<path
d="M98.92 79.0797L98.9199 30.5769M98.92 79.0797L50.7096 106.914L50.7095 58.3867M98.92 79.0797L98.9201 30.577M98.92 79.0797L50.7094 106.914M50.7094 106.914L2.49883 79.0797L2.49873 30.5767M50.7094 106.914L50.7093 58.3866M50.7095 58.3867L50.7093 58.3866M50.7095 58.3867L50.7097 58.3868L56.8966 54.8177M50.7095 58.3867L56.8966 54.8177M98.9199 30.5769L56.8966 54.8177M98.9199 30.5769L50.7093 2.7423M2.49873 30.5767L50.7093 2.7423M2.49873 30.5767L44.5221 54.8176M50.7093 58.3866L44.5221 54.8176M50.7093 58.3866L50.7091 58.3865L44.5219 54.8175M50.7093 58.3866L56.8964 54.8176L50.7091 51.2453M98.9201 30.577L56.8966 54.8177M98.9201 30.577L50.7093 2.7423M50.7093 2.7423V51.2454M50.7093 51.2454L44.5221 54.8176M50.7093 51.2454L56.8966 54.8177M50.7091 51.2453L44.5219 54.8175M50.7091 51.2453V2.74219L2.49854 30.5765L44.5219 54.8175"
@@ -50,15 +50,15 @@ const BoxIcon = () => {
<g data-lid="close" className="group-hover:hidden">
<path
d="M100.5 29.4447L50.5 0.577148L0.5 29.4446L50.5 58.3122L100.5 29.4447Z"
fill="#F09C14"
fill={color}
/>
<path
d="M100.5 37.8208V29.4447L50.5 58.3122V66.6629L100.5 37.8208Z"
fill="#F09C14"
fill={color}
/>
<path
d="M50.5 66.6629V58.3122L0.5 29.4446V37.8208L50.4998 66.6628L50.5 66.6629Z"
fill="#F09C14"
fill={color}
/>
<path
d="M50.5 66.6629V58.3122M50.5 66.6629L50.4998 66.6628L0.5 37.8208V29.4446M50.5 66.6629L100.5 37.8208V29.4447M0.5 29.4446L50.5 0.577148L100.5 29.4447M0.5 29.4446L50.5 58.3122M100.5 29.4447L50.5 58.3122"
@@ -67,19 +67,19 @@ const BoxIcon = () => {
</g>
<g data-lid="open" className="hidden group-hover:block">
<path
d="M100.5 29.4159L50.5 0.54834L0.5 18.2581L50.5 47.1256L100.5 29.4159Z"
fill="#F09C14"
d="M100.5 29.3949L50.5003 0.527344L0.5 10.5273L50.5 39.3949L100.5 29.3949Z"
fill={color}
/>
<path
d="M100.5 37.7919V29.4159L50.5 47.1256V55.4763L100.5 37.7919Z"
fill="#F09C14"
d="M100.5 37.7709V29.3949L50.5 39.3949V47.7456L100.5 37.7709Z"
fill={color}
/>
<path
d="M50.5 55.4763V47.1256L0.5 18.2581V26.6342L50.5 55.4763Z"
fill="#F09C14"
d="M50.5 47.7456V39.3949L0.5 10.5273V18.9035L50.5 47.7456Z"
fill={color}
/>
<path
d="M50.5 55.4763V47.1256M50.5 55.4763L0.5 26.6342V18.2581M50.5 55.4763L100.5 37.7919V29.4159M0.5 18.2581L50.5 0.54834L100.5 29.4159M0.5 18.2581L50.5 47.1256M100.5 29.4159L50.5 47.1256"
d="M50.5 47.7456V39.3949M50.5 47.7456L0.5 18.9035V10.5273M50.5 47.7456L100.5 37.7709V29.3949M0.5 10.5273L50.5003 0.527344L100.5 29.3949M0.5 10.5273L50.5 39.3949M100.5 29.3949L50.5 39.3949"
stroke="black"
/>
</g>

View File

@@ -53,7 +53,7 @@ const CreateBoxAction = ({ className }: CreateNewBoxProps) => {
{m.nav_add_new()}
</DialogDescription>
</DialogHeader>
<CreateNewBoxForm />
<CreateNewBoxForm onSubmit={_setOpen} />
</DialogContent>
</Dialog>
);

View File

@@ -0,0 +1,66 @@
import usePreventAutoFocus from '@/hooks/use-prevent-auto-focus';
import { m } from '@/paraglide/messages';
import { PenIcon } from '@phosphor-icons/react';
import { Button } from '@ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@ui/dialog';
import { Label } from '@ui/label';
import { Tooltip, TooltipContent, TooltipTrigger } from '@ui/tooltip';
import { useState } from 'react';
import AdminEditBoxForm from '../form/box/admin-edit-box-form';
type EditBoxProps = {
data: BoxWithCount;
};
const EditBoxDialog = ({ data }: EditBoxProps) => {
const [_open, _setOpen] = useState(false);
const prevent = usePreventAutoFocus();
return (
<Dialog open={_open} onOpenChange={_setOpen}>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon"
className="rounded-full cursor-pointer text-blue-500 hover:bg-blue-100 hover:text-blue-600"
>
<PenIcon size={16} />
<span className="sr-only">{m.ui_edit_house_btn()}</span>
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent className="bg-blue-500 [&_svg]:bg-blue-500 [&_svg]:fill-blue-500 text-white">
<Label>{m.ui_edit_house_btn()}</Label>
</TooltipContent>
</Tooltip>
<DialogContent
className="max-w-100 xl:max-w-2xl"
{...prevent}
onPointerDownOutside={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-blue-600">
<PenIcon size={16} />
{m.ui_edit_box_btn()}
</DialogTitle>
<DialogDescription className="sr-only">
{m.ui_edit_box_btn()}
</DialogDescription>
</DialogHeader>
<AdminEditBoxForm data={data} onSubmit={_setOpen} />
</DialogContent>
</Dialog>
);
};
export default EditBoxDialog;

View File

@@ -48,17 +48,14 @@ const ViewDetailBoxAction = ({ data }: ActionProps) => {
type="button"
variant="ghost"
size="icon"
className="rounded-full cursor-pointer text-blue-500 hover:bg-blue-100 hover:text-blue-600"
className="rounded-full cursor-pointer text-teal-500 hover:bg-teal-100 hover:text-teal-600"
>
<EyeIcon size={16} />
<span className="sr-only">{m.ui_view_btn()}</span>
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent
side="left"
className="bg-blue-500 [&_svg]:bg-blue-500 [&_svg]:fill-blue-500 text-white"
>
<TooltipContent className="bg-teal-500 [&_svg]:bg-teal-500 [&_svg]:fill-teal-500 text-white">
<Label>{m.ui_view_btn()}</Label>
</TooltipContent>
</Tooltip>
@@ -68,7 +65,7 @@ const ViewDetailBoxAction = ({ data }: ActionProps) => {
onPointerDownOutside={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-blue-600">
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-teal-600">
<EyeIcon size={20} />
{m.ui_dialog_view_title({ type: m.nav_boxes() })}
</DialogTitle>
@@ -106,7 +103,7 @@ const ViewDetailBoxAction = ({ data }: ActionProps) => {
></div>
</TableCell>
</TableRow>
{data.tags.length && (
{!!data.tags.length && (
<TableRow>
<TableCell>{m.boxes_page_ui_table_header_tags()}</TableCell>
<TableCell className="flex">

View File

@@ -0,0 +1,82 @@
import { useAppForm } from '@/hooks/use-app-form';
import { m } from '@/paraglide/messages';
import { updateBoxSchema, updateBoxSchemaType } from '@service/box.schema';
import { Button } from '@ui/button';
import { DialogClose, DialogFooter } from '@ui/dialog';
import { Field, FieldGroup } from '@ui/field';
type EditBoxForAdminProps = {
data: BoxWithCount;
onSubmit: (open: boolean) => void;
};
const AdminEditBoxForm = ({ data, onSubmit }: EditBoxForAdminProps) => {
const form = useAppForm({
defaultValues: {
id: data.id,
name: data.name,
description: data.description,
color: data.color || '#000000',
tags: data.tags || [],
} as updateBoxSchemaType,
validators: {
onSubmit: updateBoxSchema,
onChange: updateBoxSchema,
},
onSubmit: ({ value }) => {
console.log(value);
},
});
return (
<form
id="admin-edit-box-form"
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
form.handleSubmit();
}}
>
<FieldGroup>
<form.AppField name="name">
{(field) => <field.TextField label={m.boxes_page_form_name()} />}
</form.AppField>
<form.AppField name="description">
{(field) => (
<field.TextArea label={m.boxes_page_form_description()} />
)}
</form.AppField>
<form.AppField name="color">
{(field) => (
<field.TextField type="color" label={m.boxes_page_form_color()} />
)}
</form.AppField>
<form.AppField name="tags">
{(field) => (
<field.TagInput
label={m.boxes_page_form_tag()}
placeholder={m.boxes_page_form_tag_placeholder()}
/>
)}
</form.AppField>
<Field>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline" type="button">
{m.ui_cancel_btn()}
</Button>
</DialogClose>
<form.AppForm>
<form.SubscribeButton
label={m.ui_confirm_btn()}
// disabled={isPending}
/>
</form.AppForm>
</DialogFooter>
</Field>
</FieldGroup>
</form>
);
};
export default AdminEditBoxForm;

View File

@@ -38,7 +38,7 @@ const CreateNewBoxForm = ({ onSubmit }: CreateBoxProp) => {
const queryClient = useQueryClient();
const { mutate: createBoxMutation } = useMutation({
const { mutate: createBoxMutation, isPending } = useMutation({
mutationFn: createBox,
onSuccess: () => {
queryClient.invalidateQueries({
@@ -124,7 +124,7 @@ const CreateNewBoxForm = ({ onSubmit }: CreateBoxProp) => {
<form.AppForm>
<form.SubscribeButton
label={m.ui_confirm_btn()}
// disabled={isPending}
disabled={isPending}
/>
</form.AppForm>
</DialogFooter>

View File

@@ -0,0 +1,7 @@
type EditItemForAdminProps = {};
const AdminEditItemForm = () => {
return <div>AdminEditItemForm</div>;
};
export default AdminEditItemForm;

View File

@@ -1,14 +1,25 @@
import { m } from '@/paraglide/messages';
import { CarrotIcon } from '@phosphor-icons/react';
import { createFileRoute } from '@tanstack/react-router';
import { Card, CardHeader, CardTitle } from '@ui/card';
export const Route = createFileRoute('/(app)/(auth)/kanri/items')({
component: RouteComponent,
staticData: { breadcrumb: () => m.nav_items() },
});
function RouteComponent() {
return (
<div className="@container/main flex flex-1 flex-col gap-2 p-4">
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card *:data-[slot=card]:bg-linear-to-br *:data-[slot=card]:shadow-xs">
Hello "/(app)/(auth)/kanri/items"!
<Card>
<CardHeader>
<CardTitle className="text-xl flex items-center gap-2">
<CarrotIcon size={24} />
{m.items_page_ui_title()}
</CardTitle>
</CardHeader>
</Card>
</div>
</div>
);

View File

@@ -7,7 +7,7 @@ import { auditListSchema } from './audit.schema';
export const getAllAudit = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(auditListSchema)
.validator(auditListSchema)
.handler(async ({ data }) => {
try {
const skip = (data.page - 1) * data.limit;

View File

@@ -4,12 +4,12 @@ import { parseError } from '@/lib/errors';
import { DB_TABLE, LOG_ACTION } from '@/types/enum';
import { authMiddleware } from '@lib/middleware';
import { createServerFn } from '@tanstack/react-start';
import { boxListSchema, createBoxSchema } from './box.schema';
import { boxListSchema, createBoxSchema, updateBoxSchema } from './box.schema';
import { createAuditLog } from './repository';
export const getAllBox = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(boxListSchema)
.validator(boxListSchema)
.handler(async ({ data }) => {
try {
const { page, limit, keyword } = data;
@@ -82,7 +82,7 @@ export const getAllBox = createServerFn({ method: 'GET' })
export const createBox = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(createBoxSchema)
.validator(createBoxSchema)
.handler(async ({ data, context: { user } }) => {
try {
const { name, description, color, tags, houseId } = data;
@@ -116,3 +116,16 @@ export const createBox = createServerFn({ method: 'POST' })
throw { message, code };
}
});
export const updateBoxAdmin = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.validator(updateBoxSchema)
.handler(async ({ data, context: { user } }) => {
try {
const { id, name, description, color, tags } = data;
} catch (error) {
console.error(error);
const { message, code } = parseError(error);
throw { message, code };
}
});

View File

@@ -24,3 +24,9 @@ export const createBoxSchema = z.object({
houseId: z.string().nonempty(m.houses_page_message_house_not_found()),
tags: z.array(z.string()),
});
export const updateBoxSchema = baseBox.and(
createBoxSchema.omit({ houseId: true }),
);
export type updateBoxSchemaType = z.input<typeof updateBoxSchema>;

View File

@@ -20,7 +20,7 @@ import { createAuditLog, createNotification } from './repository';
export const getAllHouse = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(houseListSchema)
.validator(houseListSchema)
.handler(async ({ data }) => {
try {
const { page, limit, keyword } = data;
@@ -118,7 +118,7 @@ export const getCurrentUserHouses = createServerFn({ method: 'GET' })
export const getHouseForSelect = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(houseForSelectSchema)
.validator(houseForSelectSchema)
.handler(async ({ data }) => {
try {
const result = await prisma.house.findMany({
@@ -151,7 +151,7 @@ export const getHouseForSelect = createServerFn({ method: 'GET' })
export const createHouse = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(houseCreateBESchema)
.validator(houseCreateBESchema)
.handler(async ({ data, context: { user } }) => {
try {
const result = await auth.api.createOrganization({
@@ -179,7 +179,7 @@ export const createHouse = createServerFn({ method: 'POST' })
export const updateHouse = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(houseEditBESchema)
.validator(houseEditBESchema)
.handler(async ({ data, context: { user } }) => {
try {
const currentHouse = await prisma.house.findUnique({
@@ -216,7 +216,7 @@ export const updateHouse = createServerFn({ method: 'POST' })
export const deleteHouse = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(baseHouse)
.validator(baseHouse)
.handler(async ({ data, context: { user } }) => {
try {
const currentHouse = await prisma.house.findUnique({
@@ -255,7 +255,7 @@ export const deleteHouse = createServerFn({ method: 'POST' })
export const deleteUserHouse = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(baseHouse)
.validator(baseHouse)
.handler(async ({ data, context: { user } }) => {
try {
const currentHouse = await prisma.house.findUnique({
@@ -292,7 +292,7 @@ export const deleteUserHouse = createServerFn({ method: 'POST' })
export const invitationMember = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(invitationCreateBESchema)
.validator(invitationCreateBESchema)
.handler(async ({ data, context: { user } }) => {
try {
const headers = getRequestHeaders();
@@ -347,7 +347,7 @@ export const invitationMember = createServerFn({ method: 'POST' })
export const cancelInvitation = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(baseHouse)
.validator(baseHouse)
.handler(async ({ data }) => {
try {
const headers = getRequestHeaders();
@@ -381,7 +381,7 @@ export const cancelInvitation = createServerFn({ method: 'POST' })
export const acceptInvitation = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(actionInvitationSchema)
.validator(actionInvitationSchema)
.handler(async ({ data, context: { user } }) => {
try {
const result = await prisma.invitation.update({
@@ -434,7 +434,7 @@ export const acceptInvitation = createServerFn({ method: 'POST' })
export const rejectInvitation = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(actionInvitationSchema)
.validator(actionInvitationSchema)
.handler(async ({ data }) => {
try {
const headers = getRequestHeaders();
@@ -462,7 +462,7 @@ export const rejectInvitation = createServerFn({ method: 'POST' })
export const leaveHouse = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(baseHouse)
.validator(baseHouse)
.handler(async ({ data }) => {
try {
const headers = getRequestHeaders();
@@ -494,7 +494,7 @@ export const leaveHouse = createServerFn({ method: 'POST' })
export const removeMember = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(removeMemberSchema)
.validator(removeMemberSchema)
.handler(async ({ data, context: { user } }) => {
try {
const headers = getRequestHeaders();

View File

@@ -42,7 +42,7 @@ export const getTopFiveNotification = createServerFn({ method: 'GET' })
export const getAllNotifications = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(notificationListSchema)
.validator(notificationListSchema)
.handler(async ({ data, context: { user } }) => {
try {
const skip = (data.page - 1) * data.limit;

View File

@@ -12,7 +12,7 @@ import { changePasswordBESchema } from './user.schema';
export const updateProfile = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(z.instanceof(FormData))
.validator(z.instanceof(FormData))
.handler(async ({ data: formData, context: { user } }) => {
try {
let imageKey;
@@ -61,7 +61,7 @@ export const updateProfile = createServerFn({ method: 'POST' })
export const changePassword = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(changePasswordBESchema)
.validator(changePasswordBESchema)
.handler(async ({ data, context: { user } }) => {
try {
const headers = getRequestHeaders();

View File

@@ -33,7 +33,7 @@ export const getCurrentUserLanguage = createServerFn({ method: 'GET' })
});
export const updateAdminSettings = createServerFn({ method: 'POST' })
.inputValidator(settingSchema)
.validator(settingSchema)
.middleware([authMiddleware])
.handler(async ({ data, context }) => {
try {
@@ -112,7 +112,7 @@ export const getUserSettings = createServerFn({ method: 'GET' })
});
export const updateUserSettings = createServerFn({ method: 'POST' })
.inputValidator(userSettingSchema)
.validator(userSettingSchema)
.middleware([authMiddleware])
.handler(async ({ data, context }) => {
// Update each setting

View File

@@ -19,7 +19,7 @@ import {
export const getAllUser = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(userListSchema)
.validator(userListSchema)
.handler(async ({ data }) => {
try {
const headers = getRequestHeaders();
@@ -58,7 +58,7 @@ export const getAllUser = createServerFn({ method: 'GET' })
export const getUserForSelect = createServerFn({ method: 'GET' })
.middleware([authMiddleware])
.inputValidator(userForSelectSchema)
.validator(userForSelectSchema)
.handler(async ({ data, context: { user } }) => {
try {
const { keyword, noself } = data;
@@ -106,7 +106,7 @@ export const getUserForSelect = createServerFn({ method: 'GET' })
export const setUserPassword = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(userSetPasswordSchema)
.validator(userSetPasswordSchema)
.handler(async ({ data, context: { user } }) => {
try {
const headers = getRequestHeaders();
@@ -137,7 +137,7 @@ export const setUserPassword = createServerFn({ method: 'POST' })
export const updateUserInformation = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(userUpdateInfoSchema)
.validator(userUpdateInfoSchema)
.handler(async ({ data, context: { user } }) => {
try {
const currentUser = await prisma.user.findUnique({
@@ -176,7 +176,7 @@ export const updateUserInformation = createServerFn({ method: 'POST' })
export const setUserRole = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(userUpdateRoleBESchema)
.validator(userUpdateRoleBESchema)
.handler(async ({ data, context: { user } }) => {
try {
const currentUser = await prisma.user.findUnique({
@@ -215,7 +215,7 @@ export const setUserRole = createServerFn({ method: 'POST' })
export const banUser = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(userBanSchema)
.validator(userBanSchema)
.handler(async ({ data, context: { user } }) => {
try {
const headers = getRequestHeaders();
@@ -251,7 +251,7 @@ export const banUser = createServerFn({ method: 'POST' })
export const unbanUser = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(baseUser)
.validator(baseUser)
.handler(async ({ data, context: { user } }) => {
try {
const headers = getRequestHeaders();
@@ -282,7 +282,7 @@ export const unbanUser = createServerFn({ method: 'POST' })
export const createUser = createServerFn({ method: 'POST' })
.middleware([authMiddleware])
.inputValidator(userCreateBESchema)
.validator(userCreateBESchema)
.handler(async ({ data, context: { user } }) => {
try {
const result = await auth.api.createUser({

View File

@@ -4,12 +4,14 @@ import { devtools } from '@tanstack/devtools-vite';
import { tanstackStart } from '@tanstack/react-start/plugin/vite';
import viteReact from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import viteTsConfigPaths from 'vite-tsconfig-paths';
const config = defineConfig({
// server: {
// host: true,
// },
resolve: {
tsconfigPaths: true,
},
plugins: [
paraglideVitePlugin({
project: './project.inlang',
@@ -19,10 +21,6 @@ const config = defineConfig({
strategy: ['cookie', 'baseLocale'],
}),
devtools(),
// this is the plugin that enables path aliases
viteTsConfigPaths({
projects: ['./tsconfig.json'],
}),
tailwindcss(),
tanstackStart(),
viteReact(),