From 0394d42d0246869e56034c15c593101552042078 Mon Sep 17 00:00:00 2001 From: Berkay Date: Sat, 29 Nov 2025 16:57:17 +0300 Subject: [PATCH] updated build --- backend/src/builds/dto/create-build.input.ts | 5 +- backend/src/builds/dto/list-build.response.ts | 1 + backend/src/builds/dto/update-build.input.ts | 3 - backend/src/models/user.model.ts | 29 +- backend/src/users/dto/create-user.input.ts | 23 +- backend/src/users/dto/update-user.input.ts | 22 +- frontend/README.md | 62 ++- frontend/components/dashboard/nav-main.tsx | 8 +- frontend/components/sidebar/app-sidebar.tsx | 21 +- frontend/pages/users/add/form.tsx | 396 +++++++----------- frontend/pages/users/add/queries.tsx | 16 +- frontend/pages/users/add/schema.ts | 10 - frontend/pages/users/add/types.ts | 1 - frontend/pages/users/page.tsx | 2 + frontend/pages/users/selections/addPage.tsx | 42 ++ .../pages/users/selections/builds/columns.tsx | 146 +++++++ .../users/selections/builds/data-table.tsx | 276 ++++++++++++ .../pages/users/selections/builds/page.tsx | 45 ++ .../pages/users/selections/builds/queries.tsx | 36 ++ .../pages/users/selections/builds/schema.tsx | 31 ++ .../users/selections/companies/columns.tsx | 134 ++++++ .../users/selections/companies/data-table.tsx | 272 ++++++++++++ .../pages/users/selections/companies/page.tsx | 41 ++ .../users/selections/companies/queries.tsx | 19 + .../users/selections/companies/schema.tsx | 27 ++ frontend/pages/users/update/form.tsx | 364 +++++++--------- frontend/pages/users/update/queries.tsx | 8 +- frontend/pages/users/update/schema.ts | 13 +- 28 files changed, 1502 insertions(+), 551 deletions(-) create mode 100644 frontend/pages/users/selections/addPage.tsx create mode 100644 frontend/pages/users/selections/builds/columns.tsx create mode 100644 frontend/pages/users/selections/builds/data-table.tsx create mode 100644 frontend/pages/users/selections/builds/page.tsx create mode 100644 frontend/pages/users/selections/builds/queries.tsx create mode 100644 frontend/pages/users/selections/builds/schema.tsx create mode 100644 frontend/pages/users/selections/companies/columns.tsx create mode 100644 frontend/pages/users/selections/companies/data-table.tsx create mode 100644 frontend/pages/users/selections/companies/page.tsx create mode 100644 frontend/pages/users/selections/companies/queries.tsx create mode 100644 frontend/pages/users/selections/companies/schema.tsx diff --git a/backend/src/builds/dto/create-build.input.ts b/backend/src/builds/dto/create-build.input.ts index e7e1358..e09074c 100644 --- a/backend/src/builds/dto/create-build.input.ts +++ b/backend/src/builds/dto/create-build.input.ts @@ -1,5 +1,5 @@ import { ExpiryBaseInput } from "@/models/base.model"; -import { InputType, Field, ID } from "@nestjs/graphql"; +import { InputType, Field } from "@nestjs/graphql"; @InputType() export class CreateBuildInfoInput { @@ -60,9 +60,6 @@ export class CreateBuildInput extends ExpiryBaseInput { @Field() buildType: string; - @Field() - collectionToken: string; - @Field(() => CreateBuildInfoInput) info: CreateBuildInfoInput; diff --git a/backend/src/builds/dto/list-build.response.ts b/backend/src/builds/dto/list-build.response.ts index 31a8d12..8c14b2c 100644 --- a/backend/src/builds/dto/list-build.response.ts +++ b/backend/src/builds/dto/list-build.response.ts @@ -39,6 +39,7 @@ export class ListPartialIbanResponse { @ObjectType() class ResponsibleCompanyPerson { + @Field(() => Company, { nullable: true }) company?: Company; diff --git a/backend/src/builds/dto/update-build.input.ts b/backend/src/builds/dto/update-build.input.ts index aa16012..c6884b4 100644 --- a/backend/src/builds/dto/update-build.input.ts +++ b/backend/src/builds/dto/update-build.input.ts @@ -102,9 +102,6 @@ export class UpdateBuildInput extends ExpiryBaseInput { @Field({ nullable: true }) buildType?: string; - @Field({ nullable: true }) - collectionToken?: string; - @Field(() => UpdateBuildInfoInput, { nullable: true }) info?: UpdateBuildInfoInput; diff --git a/backend/src/models/user.model.ts b/backend/src/models/user.model.ts index a70bb71..aaa4c82 100644 --- a/backend/src/models/user.model.ts +++ b/backend/src/models/user.model.ts @@ -4,30 +4,21 @@ import { Document, Types } from 'mongoose'; import { Base } from '@/models/base.model'; import { Person } from '@/models/person.model'; -@ObjectType() -export class CollectionTokenItem { - - @Field() - @Prop({ required: true }) - prefix: string; - - @Field() - @Prop({ required: true }) - token: string; -} - @ObjectType() export class CollectionToken { - @Field(() => [CollectionTokenItem]) - @Prop({ type: [CollectionTokenItem], default: [] }) - tokens: CollectionTokenItem[]; - @Field() - @Prop({ required: true, default: '' }) - default: string; + defaultSelection: string; + + @Field(() => [String]) + selectedBuildIDS: string[]; + + @Field(() => [String]) + selectedCompanyIDS: string[]; + } + @ObjectType() @Schema({ timestamps: true }) export class User extends Base { @@ -68,7 +59,7 @@ export class User extends Base { phone: string; @Field(() => CollectionToken) - @Prop({ type: CollectionToken, default: () => ({ tokens: [], default: '' }) }) + @Prop({ type: CollectionToken, default: () => ({ defaultSelection: '', selectedBuildIDS: [], selectedCompanyIDS: [] }) }) collectionTokens: CollectionToken; @Field(() => ID) diff --git a/backend/src/users/dto/create-user.input.ts b/backend/src/users/dto/create-user.input.ts index ed2ad34..75feb72 100644 --- a/backend/src/users/dto/create-user.input.ts +++ b/backend/src/users/dto/create-user.input.ts @@ -1,21 +1,17 @@ import { InputType, Field, ID } from '@nestjs/graphql'; @InputType() -export class CollectionTokenItemInput { - @Field() - prefix: string; +export class CreateCollectionTokenInput { @Field() - token: string; -} + defaultSelection: string; -@InputType() -export class CollectionTokenInput { - @Field(() => [CollectionTokenItemInput]) - tokens: CollectionTokenItemInput[]; + @Field(() => [String]) + selectedBuildIDS: string[]; + + @Field(() => [String]) + selectedCompanyIDS: string[]; - @Field() - default: string; } @InputType() @@ -39,8 +35,8 @@ export class CreateUserInput { @Field() phone: string; - @Field(() => CollectionTokenInput) - collectionTokens: CollectionTokenInput; + @Field(() => CreateCollectionTokenInput) + collectionTokens: CreateCollectionTokenInput; @Field(() => ID) person: string; @@ -59,4 +55,5 @@ export class CreateUserInput { @Field(() => Boolean, { nullable: true }) isNotificationSend?: boolean; + } diff --git a/backend/src/users/dto/update-user.input.ts b/backend/src/users/dto/update-user.input.ts index e60ab3c..68764de 100644 --- a/backend/src/users/dto/update-user.input.ts +++ b/backend/src/users/dto/update-user.input.ts @@ -1,23 +1,21 @@ import { InputType, Field, ID } from '@nestjs/graphql'; -@InputType() -export class UpdateCollectionTokenItemInput { - @Field({ nullable: true }) - prefix?: string; - - @Field({ nullable: true }) - token?: string; -} @InputType() export class UpdateCollectionTokenInput { - @Field(() => [UpdateCollectionTokenItemInput], { nullable: true }) - tokens?: UpdateCollectionTokenItemInput[]; - @Field({ nullable: true }) - default?: string; + @Field() + defaultSelection: string; + + @Field(() => [String]) + selectedBuildIDS: string[]; + + @Field(() => [String]) + selectedCompanyIDS: string[]; + } + @InputType() export class UpdateUserInput { diff --git a/frontend/README.md b/frontend/README.md index d1d4503..17cfb2a 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,9 +1,57 @@ # ToDo's -Build: -BuildPart: -Build Living Space: -People: -User: -Company: -ADD UPDATE yapan page: +Addition of Living Space Test Page + +## RoadMap of Living Space Addition + +### 1 User Types + +-- Create User Types + +Type: Employee +Token: L9wBdwV9OlxsLAghlo3vj2pAQpqFDBw7cm9znQ +Type Token: L9wBdwV9OlxsLAgh +Description: Application Manager Employee + +### 2 People + +-- Create People + +First Name: Berkay +Surname: Karatay +Middle Name: +Sex Code: M +Person Ref: SomeRef1 +Person Tag: SomeTag1 +Father Name: Mehmet +Mother Name: Selma +Country Code: TR +National Identity Id: 12345678901 +Birth Place: Ankara +Birth Date: 1999-01-01 +Tax No: 123456789 +Birthname: +Expiry Starts: 01/01/1990 +Expiry Ends: 01/01/2099 + +### 3 Users + +-- Create Users + +password: string +rePassword:string +tag: berkai +email: @url:karatay.berkay@gmail.com +phone: +905555555555 +defaultSelection: "SomeBuild|CompanyID" +selectedBuildingIDS | selectedCompanyIDS: ["SomeBuild1|CompanyID1", "SomeBuild2|CompanyID2", "SomeBuild3|CompanyID3"] +expiryStarts: "01/01/2025" +expiryEnds: "01/01/2026" +isConfirmed: true +isNotificationSend: true + +### 4 Building + +-- Create Building + +asd diff --git a/frontend/components/dashboard/nav-main.tsx b/frontend/components/dashboard/nav-main.tsx index 4c1a374..07962d0 100644 --- a/frontend/components/dashboard/nav-main.tsx +++ b/frontend/components/dashboard/nav-main.tsx @@ -4,11 +4,7 @@ import { type Icon } from "@tabler/icons-react" import { SidebarGroup, SidebarGroupContent, SidebarMenu, SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar" import { usePathname } from 'next/navigation' -interface NavMainProps { - title: string - url: string - icon?: Icon -} +interface NavMainProps { title: string, url: string, icon?: Icon } export function NavMain({ items }: { items: NavMainProps[] }) { const pathname = usePathname()?.split("/")[1] @@ -17,7 +13,7 @@ export function NavMain({ items }: { items: NavMainProps[] }) { {item.icon && }{item.title} const linkRenderDisabled = (item: NavMainProps) => - + {item.icon && }{item.title} return ( diff --git a/frontend/components/sidebar/app-sidebar.tsx b/frontend/components/sidebar/app-sidebar.tsx index 2698752..43f36ca 100644 --- a/frontend/components/sidebar/app-sidebar.tsx +++ b/frontend/components/sidebar/app-sidebar.tsx @@ -28,15 +28,20 @@ const data = { }, navMain: [ { - title: "Users", - url: "/users", - icon: IconUsers + title: "User Types", + url: "/user-types", + icon: IconFileCertificate }, { title: "People", url: "/people", icon: IconListDetails }, + { + title: "Users", + url: "/users", + icon: IconUsers + }, { title: "Build", url: "/builds", @@ -77,11 +82,6 @@ const data = { url: "/living-spaces", icon: IconHome2 }, - { - title: "User Types", - url: "/user-types", - icon: IconFileCertificate - } ], navClouds: [ // { @@ -173,10 +173,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { - + Acme Inc. diff --git a/frontend/pages/users/add/form.tsx b/frontend/pages/users/add/form.tsx index 19db98c..2005192 100644 --- a/frontend/pages/users/add/form.tsx +++ b/frontend/pages/users/add/form.tsx @@ -1,5 +1,6 @@ "use client" -import { useFieldArray, useForm } from "react-hook-form" +import { useState, useEffect } from "react" +import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { userAddSchema, type UserAdd } from "./schema" import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form" @@ -9,6 +10,7 @@ import { Checkbox } from "@/components/ui/checkbox" import { Separator } from "@/components/ui/separator" import { useAddUserMutation } from "./queries" import { DateTimePicker } from "@/components/ui/date-time-picker" +import PageAddUserSelections from "../selections/addPage" const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { const form = useForm({ @@ -22,258 +24,174 @@ const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { rePassword: "", tag: "", email: "", - phone: "", - collectionTokens: { - default: "", - tokens: [], - }, + phone: "" }, }) - const { control, handleSubmit } = form + const [defaultSelection, setDefaultSelection] = useState("") + const [selectedBuildIDS, setSelectedBuildIDS] = useState([]) + const [selectedCompanyIDS, setSelectedCompanyIDS] = useState([]) - const { fields, append, remove } = useFieldArray({ control, name: "collectionTokens.tokens" }) + const appendBuildID = (id: string) => setSelectedBuildIDS((prev) => (id && !selectedBuildIDS.includes(id) ? [...prev, id] : prev)) + const appendCompanyID = (id: string) => setSelectedCompanyIDS((prev) => (id && !selectedCompanyIDS.includes(id) ? [...prev, id] : prev)) + const removeBuildID = (id: string) => setSelectedBuildIDS((prev) => prev.filter((item) => item !== id)) + const removeCompanyID = (id: string) => setSelectedCompanyIDS((prev) => prev.filter((item) => item !== id)) + + const { handleSubmit } = form const mutation = useAddUserMutation(); - - function onSubmit(values: UserAdd) { mutation.mutate(values as any); setTimeout(() => refetchTable(), 400) } + function onSubmit(values: UserAdd) { mutation.mutate({ data: values as any, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }); } return ( -
- - {/* BASIC INFO */} -
- ( - - Email - - - - - - )} - /> - - ( - - Phone - - - - - - )} - /> -
- - {/* PASSWORD / TAG */} -
- ( - - Password - - - - - - )} - /> - ( - - Re-Password - - - - - - )} - /> - - ( - - Tag - - - - - - )} - /> -
- - {/* DATES */} -
- - ( - - Expiry Starts - - - - - - )} - /> - - ( - - Expiry Ends - - - - - - )} - /> -
- - {/* SWITCHES */} -
- ( - - - - - Confirmed - - )} - /> - - ( - - - - - Send Notification - - )} - /> -
- - - - {/* COLLECTION TOKENS */} - ( - - Default Token - - - - - )} - /> - -
-
- Tokens - - +
+ + + + {/* BASIC INFO */} +
+ ( + + Email + + + + + + )} + /> + ( + + Phone + + + + + + )} + />
- {fields.length === 0 && ( -

No tokens added.

- )} + {/* PASSWORD / TAG */} +
+ ( + + Password + + + + + + )} + /> + ( + + Re-Password + + + + + + )} + /> - {fields.map((fieldItem, i) => ( -
-
- ( - - Prefix - - - - - - )} - /> -
- -
- ( - - Token - - - - - - )} - /> -
- - + ( + + Tag + + + + + + )} + /> + {/* SWITCHES */} +
+ ( + + + + + Confirmed + + )} + /> + ( + + + + + Send Notification + + )} + />
- ))} -
+
- + {/* DATES */} +
+ ( + + Expiry Starts + + + + + + )} + /> + ( + + Expiry Ends + + + + + + )} + /> +
- - - + + + + +
) } diff --git a/frontend/pages/users/add/queries.tsx b/frontend/pages/users/add/queries.tsx index c52c3c4..4bdbf27 100644 --- a/frontend/pages/users/add/queries.tsx +++ b/frontend/pages/users/add/queries.tsx @@ -3,21 +3,29 @@ import { useMutation } from '@tanstack/react-query' import { UserAdd } from './types' import { toISOIfNotZ } from '@/lib/utils' -const fetchGraphQlUsersAdd = async (record: UserAdd): Promise<{ data: UserAdd | null; status: number }> => { - console.log('Fetching test data from local API'); +const fetchGraphQlUsersAdd = async ( + record: UserAdd, + selectedBuildIDS: string[], + selectedCompanyIDS: string[], + defaultSelection: string, + refetchTable: () => void +): Promise<{ data: UserAdd | null; status: number }> => { record.expiryStarts = toISOIfNotZ(record.expiryStarts); record.expiryEnds = toISOIfNotZ(record.expiryEnds); try { - const res = await fetch('/api/users/add', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(record) }); + const res = await fetch('/api/users/add', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ ...record, selectedBuildIDS, selectedCompanyIDS, defaultSelection }) }); if (!res.ok) { const errorText = await res.text(); console.error('Test data API error:', errorText); throw new Error(`API error: ${res.status} ${res.statusText}`) } const data = await res.json(); + refetchTable(); return { data: data.data, status: res.status } } catch (error) { console.error('Error fetching test data:', error); throw error } }; export function useAddUserMutation() { return useMutation({ - mutationFn: (data: UserAdd) => fetchGraphQlUsersAdd(data), + mutationFn: ( + { data, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }: { data: UserAdd, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, refetchTable: () => void } + ) => fetchGraphQlUsersAdd(data, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable), onSuccess: () => { console.log("User created successfully") }, onError: (error) => { console.error("Create user failed:", error) }, }) diff --git a/frontend/pages/users/add/schema.ts b/frontend/pages/users/add/schema.ts index 20c8ff7..5348cda 100644 --- a/frontend/pages/users/add/schema.ts +++ b/frontend/pages/users/add/schema.ts @@ -1,14 +1,5 @@ import { z } from "zod" -export const tokenSchema = z.object({ - prefix: z.string().min(1, "Prefix is required"), - token: z.string().min(1, "Token is required"), -}) - -export const collectionTokensSchema = z.object({ - default: z.string().optional(), - tokens: z.array(tokenSchema) -}) export const userAddSchema = z.object({ expiryStarts: z.string().optional(), @@ -23,7 +14,6 @@ export const userAddSchema = z.object({ email: z.string().email(), phone: z.string().min(5), - collectionTokens: collectionTokensSchema, }) export type UserAdd = z.infer diff --git a/frontend/pages/users/add/types.ts b/frontend/pages/users/add/types.ts index f14fe7b..f6699aa 100644 --- a/frontend/pages/users/add/types.ts +++ b/frontend/pages/users/add/types.ts @@ -17,7 +17,6 @@ interface UserAdd { tag: string; email: string; phone: string; - collectionTokens: CollectionTokens; } diff --git a/frontend/pages/users/page.tsx b/frontend/pages/users/page.tsx index 9cc034c..d7a5972 100644 --- a/frontend/pages/users/page.tsx +++ b/frontend/pages/users/page.tsx @@ -4,6 +4,7 @@ import { useState } from 'react'; import { UserDataTable } from '@/pages/users/table/data-table'; const PageUsers = () => { + const [page, setPage] = useState(1); const [limit, setLimit] = useState(10); const [sort, setSort] = useState({ createdAt: 'desc' }); @@ -13,6 +14,7 @@ const PageUsers = () => { const handlePageChange = (newPage: number) => { setPage(newPage) }; const handlePageSizeChange = (newSize: number) => { setLimit(newSize); setPage(1) }; + if (isLoading) { return
Loading...
} if (error) { return
Error loading users
} diff --git a/frontend/pages/users/selections/addPage.tsx b/frontend/pages/users/selections/addPage.tsx new file mode 100644 index 0000000..6ab8649 --- /dev/null +++ b/frontend/pages/users/selections/addPage.tsx @@ -0,0 +1,42 @@ +'use client'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import PageUsersCompanyTableSection from "./companies/page"; +import PageUsersBuildingTableSection from "./builds/page"; + +const PageAddUserSelections = ({ + selectedCompanyIDS, + selectedBuildingIDS, + defaultSelection, + appendCompanyID, + appendBuildingID, + removeCompanyID, + removeBuildingID, + setDefaultSelection +}: { + selectedCompanyIDS: string[]; + selectedBuildingIDS: string[]; + defaultSelection: string; + appendCompanyID: (id: string) => void; + appendBuildingID: (id: string) => void; + removeCompanyID: (id: string) => void; + removeBuildingID: (id: string) => void; + setDefaultSelection: (id: string) => void; +}) => { + const tabsClassName = "border border-gray-300 rounded-sm h-10" + return ( +
+ + + Append Builds + Append Company + +
+ + +
+
+
+ ); +}; + +export default PageAddUserSelections; diff --git a/frontend/pages/users/selections/builds/columns.tsx b/frontend/pages/users/selections/builds/columns.tsx new file mode 100644 index 0000000..850094e --- /dev/null +++ b/frontend/pages/users/selections/builds/columns.tsx @@ -0,0 +1,146 @@ +"use client" +import { z } from "zod" +import { Button } from "@/components/ui/button" +import { useSortable } from "@dnd-kit/sortable" +import { ColumnDef, flexRender, Row } from "@tanstack/react-table" +import { TableCell, TableRow } from "@/components/ui/table" +import { CSS } from "@dnd-kit/utilities" +import { schema, schemaType } from "./schema" +import { dateToLocaleString } from "@/lib/utils" +import { Pencil, Trash } from "lucide-react" +import { IconCircleMinus, IconHandClick } from "@tabler/icons-react" + +export function DraggableRow({ row, selectedIDs }: { row: Row>; selectedIDs: string[] }) { + const { transform, transition, setNodeRef, isDragging } = useSortable({ id: row.original._id }) + return ( + + {row.getVisibleCells().map((cell) => ( + {flexRender(cell.column.columnDef.cell, cell.getContext())} + ))} + + ) +} + +function getColumns(appendBuildID: (id: string) => void, removeBuildID: (id: string) => void, selectedBuildIDS: string[], defaultSelection: string, setDefaultSelection: (id: string) => void): ColumnDef[] { + return [ + { + accessorKey: "_id", + header: "ID", + }, + { + accessorKey: "buildType.token", + header: "Token", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "collectionToken", + header: "Collection Token", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.govAddressCode", + header: "Gov Address Code", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.buildName", + header: "Build Name", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.buildNo", + header: "Build No", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.maxFloor", + header: "Max Floor", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.undergroundFloor", + header: "Underground Floor", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.buildDate", + header: "Build Date", + cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", + }, + { + accessorKey: "info.decisionPeriodDate", + header: "Decision Period Date", + cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", + }, + { + accessorKey: "info.taxNo", + header: "Tax No", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.liftCount", + header: "Lift Count", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "info.heatingSystem", + header: "Heating System", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "info.coolingSystem", + header: "Cooling System", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "info.hotWaterSystem", + header: "Hot Water System", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "info.blockServiceManCount", + header: "Block Service Man Count", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.securityServiceManCount", + header: "Security Service Man Count", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.garageCount", + header: "Garage Count", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "info.managementRoomId", + header: "Management Room ID", + cell: ({ getValue }) => getValue(), + }, + { + id: "actions", + header: "Actions", + cell: ({ row }) => { + return ( + selectedBuildIDS.includes(row.original._id) ? ( +
+ +
+ ) : +
+ +
+ ); + }, + } + ] +} + +export { getColumns }; \ No newline at end of file diff --git a/frontend/pages/users/selections/builds/data-table.tsx b/frontend/pages/users/selections/builds/data-table.tsx new file mode 100644 index 0000000..35d79d5 --- /dev/null +++ b/frontend/pages/users/selections/builds/data-table.tsx @@ -0,0 +1,276 @@ +"use client" + +import * as React from "react" +import { + closestCenter, + DndContext, + KeyboardSensor, + MouseSensor, + TouchSensor, + useSensor, + useSensors, + type UniqueIdentifier, +} from "@dnd-kit/core" +import { restrictToVerticalAxis } from "@dnd-kit/modifiers" +import { + SortableContext, + verticalListSortingStrategy, +} from "@dnd-kit/sortable" +import { + IconChevronDown, + IconChevronLeft, + IconChevronRight, + IconChevronsLeft, + IconChevronsRight, + IconLayoutColumns, + IconPlus, +} from "@tabler/icons-react" +import { + ColumnFiltersState, + flexRender, + getCoreRowModel, + getFacetedRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getSortedRowModel, + SortingState, + useReactTable, + VisibilityState, +} from "@tanstack/react-table" +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { Label } from "@/components/ui/label" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@/components/ui/tabs" +import { schemaType } from "./schema" +import { getColumns, DraggableRow } from "./columns" +import { useRouter } from "next/navigation" + +export function UsersBuildDataTable({ + data, + totalCount, + currentPage = 1, + pageSize = 10, + onPageChange, + onPageSizeChange, + refetchTable, + selectedBuildIDS, + appendBuildID, + removeBuildID, + additionButtons, + defaultSelection, + setDefaultSelection +}: { + data: schemaType[], + totalCount: number, + currentPage?: number, + pageSize?: number, + onPageChange: (page: number) => void, + onPageSizeChange: (size: number) => void, + refetchTable: () => void, + selectedBuildIDS: string[], + appendBuildID: (id: string) => void, + removeBuildID: (id: string) => void, + additionButtons: React.ReactNode, + defaultSelection: string, + setDefaultSelection: (id: string) => void +}) { + + const router = useRouter(); + const [rowSelection, setRowSelection] = React.useState({}) + const [columnVisibility, setColumnVisibility] = React.useState({}) + const [columnFilters, setColumnFilters] = React.useState([]) + const [sorting, setSorting] = React.useState([]) + const sortableId = React.useId() + const sensors = useSensors(useSensor(MouseSensor, {}), useSensor(TouchSensor, {}), useSensor(KeyboardSensor, {})) + const dataIds = React.useMemo(() => data?.map(({ _id }) => _id) || [], [data]) + + const columns = getColumns(appendBuildID, removeBuildID, selectedBuildIDS, defaultSelection, setDefaultSelection); + const pagination = React.useMemo(() => ({ pageIndex: currentPage - 1, pageSize: pageSize }), [currentPage, pageSize]) + const totalPages = Math.ceil(totalCount / pageSize) + + const table = useReactTable({ + data, + columns, + pageCount: totalPages, + state: { sorting, columnVisibility, rowSelection, columnFilters, pagination }, + manualPagination: true, + getRowId: (row) => row._id.toString(), + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + onColumnVisibilityChange: setColumnVisibility, + onPaginationChange: (updater) => { const nextPagination = typeof updater === "function" ? updater(pagination) : updater; onPageChange(nextPagination.pageIndex + 1); onPageSizeChange(nextPagination.pageSize) }, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getSortedRowModel: getSortedRowModel(), + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + }) + + const handlePageSizeChange = (value: string) => { const newSize = Number(value); onPageSizeChange(newSize); onPageChange(1) } + + return ( + +
+ + +
+ + + + + + {table.getAllColumns().filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide()).map((column) => { + return ( + column.toggleVisibility(!!value)} > + {column.id} + + ) + })} + + {additionButtons && additionButtons} + +
+
+ +
+ + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + {table.getRowModel().rows.map((row) => )} + ) : ( + No results. + )} + +
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+
+ + +
+
+ Page {currentPage} of {totalPages} +
+
+ Total Count: {totalCount} +
+
+ + + + +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/frontend/pages/users/selections/builds/page.tsx b/frontend/pages/users/selections/builds/page.tsx new file mode 100644 index 0000000..e0f823d --- /dev/null +++ b/frontend/pages/users/selections/builds/page.tsx @@ -0,0 +1,45 @@ +'use client'; +import { useState } from "react"; +import { useGraphQlBuildsList } from "./queries"; +import { UsersBuildDataTable } from "./data-table"; + +const PageUsersBuildsTableSection = ( + { + selectedBuildIDS, + defaultSelection, + appendBuildID, + removeBuildID, + additionButtons, + setDefaultSelection + }: { + selectedBuildIDS: string[]; + defaultSelection: string; + appendBuildID: (id: string) => void; + removeBuildID: (id: string) => void; + additionButtons?: React.ReactNode | null; + setDefaultSelection: (id: string) => void; + } +) => { + + const [page, setPage] = useState(1); + const [limit, setLimit] = useState(10); + const [sort, setSort] = useState({ createdAt: 'desc' }); + const [filters, setFilters] = useState({}); + const { data, isLoading, error, refetch } = useGraphQlBuildsList({ limit, skip: (page - 1) * limit, sort, filters }); + + const handlePageChange = (newPage: number) => { setPage(newPage) }; + const handlePageSizeChange = (newSize: number) => { setLimit(newSize); setPage(1) }; + + if (isLoading) { return
Loading...
} + if (error) { return
Error loading users
} + + return <> + + ; + +} + +export default PageUsersBuildsTableSection; diff --git a/frontend/pages/users/selections/builds/queries.tsx b/frontend/pages/users/selections/builds/queries.tsx new file mode 100644 index 0000000..90e53e2 --- /dev/null +++ b/frontend/pages/users/selections/builds/queries.tsx @@ -0,0 +1,36 @@ +'use client' +import { useQuery, useMutation } from '@tanstack/react-query' +import { ListArguments } from '@/types/listRequest' + +const fetchGraphQlBuildsList = async (params: ListArguments): Promise => { + console.log('Fetching test data from local API'); + const { limit, skip, sort, filters } = params; + try { + const res = await fetch('/api/builds/list', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ limit, skip, sort, filters }) }); + if (!res.ok) { const errorText = await res.text(); console.error('Test data API error:', errorText); throw new Error(`API error: ${res.status} ${res.statusText}`) } + const data = await res.json(); + return { data: data.data, totalCount: data.totalCount } + } catch (error) { console.error('Error fetching test data:', error); throw error } +}; + +const fetchGraphQlDeleteBuild = async (uuid: string): Promise => { + console.log('Fetching test data from local API'); + try { + const res = await fetch(`/api/builds/delete?uuid=${uuid}`, { method: 'GET', cache: 'no-store', credentials: "include" }); + if (!res.ok) { const errorText = await res.text(); console.error('Test data API error:', errorText); throw new Error(`API error: ${res.status} ${res.statusText}`) } + const data = await res.json(); + return data + } catch (error) { console.error('Error fetching test data:', error); throw error } +}; + +export function useGraphQlBuildsList(params: ListArguments) { + return useQuery({ queryKey: ['graphql-builds-list', params], queryFn: () => fetchGraphQlBuildsList(params) }) +} + +export function useDeleteBuildMutation() { + return useMutation({ + mutationFn: ({ uuid }: { uuid: string }) => fetchGraphQlDeleteBuild(uuid), + onSuccess: () => { console.log("Person deleted successfully") }, + onError: (error) => { console.error("Delete person failed:", error) }, + }) +} diff --git a/frontend/pages/users/selections/builds/schema.tsx b/frontend/pages/users/selections/builds/schema.tsx new file mode 100644 index 0000000..563da7b --- /dev/null +++ b/frontend/pages/users/selections/builds/schema.tsx @@ -0,0 +1,31 @@ +import { z } from "zod"; + +export const schema = z.object({ + _id: z.string(), + buildType: z.object({ + token: z.string(), + typeToken: z.string(), + type: z.string(), + }), + collectionToken: z.string(), + info: z.object({ + govAddressCode: z.string(), + buildName: z.string(), + buildNo: z.string(), + maxFloor: z.number(), + undergroundFloor: z.number(), + buildDate: z.string(), + decisionPeriodDate: z.string(), + taxNo: z.string(), + liftCount: z.number(), + heatingSystem: z.boolean(), + coolingSystem: z.boolean(), + hotWaterSystem: z.boolean(), + blockServiceManCount: z.number(), + securityServiceManCount: z.number(), + garageCount: z.number(), + managementRoomId: z.number(), + }) +}); + +export type schemaType = z.infer; diff --git a/frontend/pages/users/selections/companies/columns.tsx b/frontend/pages/users/selections/companies/columns.tsx new file mode 100644 index 0000000..dc41329 --- /dev/null +++ b/frontend/pages/users/selections/companies/columns.tsx @@ -0,0 +1,134 @@ +"use client" +import { z } from "zod" +import { Button } from "@/components/ui/button" +import { Drawer, DrawerClose, DrawerContent, DrawerFooter, DrawerHeader, DrawerTrigger } from "@/components/ui/drawer" +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { useSortable } from "@dnd-kit/sortable" +import { IconCircleMinus, IconGripVertical, IconHandClick } from "@tabler/icons-react" +import { useIsMobile } from "@/hooks/use-mobile" +import { Separator } from "@/components/ui/separator" +import { ColumnDef, flexRender, Row } from "@tanstack/react-table" +import { TableCell, TableRow } from "@/components/ui/table" +import { CSS } from "@dnd-kit/utilities" +import { schema, schemaType } from "./schema" +import { dateToLocaleString } from "@/lib/utils" +import { Pencil, Trash } from "lucide-react" + + +export function DraggableRow({ row, selectedIDs }: { row: Row>; selectedIDs: string[] }) { + const { transform, transition, setNodeRef, isDragging } = useSortable({ id: row.original._id }) + return ( + + {row.getVisibleCells().map((cell) => ( + {flexRender(cell.column.columnDef.cell, cell.getContext())} + ))} + + ) +} + +function getColumns(appendCompanyID: (id: string) => void, removeCompanyID: (id: string) => void, selectedCompanyIDS: string[], defaultSelection: string, setDefaultSelection: (id: string) => void): ColumnDef[] { + return [ + { + accessorKey: "uuid", + header: "UUID", + cell: ({ getValue }) => (
{String(getValue())}
), + }, + { + accessorKey: "formal_name", + header: "Formal Name", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "company_type", + header: "Company Type", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "commercial_type", + header: "Commercial Type", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "tax_no", + header: "Tax No", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "public_name", + header: "Public Name", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "company_tag", + header: "Company Tag", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "default_lang_type", + header: "Default Language", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "default_money_type", + header: "Default Money Type", + cell: ({ getValue }) => getValue(), + }, + { + accessorKey: "is_commercial", + header: "Is Commercial", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "is_blacklist", + header: "Is Blacklist", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "createdAt", + header: "Created", + cell: ({ getValue }) => dateToLocaleString(getValue() as string), + }, + { + accessorKey: "updatedAt", + header: "Updated", + cell: ({ getValue }) => dateToLocaleString(getValue() as string), + }, + { + accessorKey: "expiryStarts", + header: "Expiry Starts", + cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", + }, + { + accessorKey: "expiryEnds", + header: "Expiry Ends", + cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", + }, + { + id: "actions", + header: "Actions", + cell: ({ row }) => { + return ( + selectedCompanyIDS.includes(row.original._id) ? ( +
+ +
+ ) : +
+ +
+ ); + }, + } + ] +} + +export { getColumns }; \ No newline at end of file diff --git a/frontend/pages/users/selections/companies/data-table.tsx b/frontend/pages/users/selections/companies/data-table.tsx new file mode 100644 index 0000000..9084a21 --- /dev/null +++ b/frontend/pages/users/selections/companies/data-table.tsx @@ -0,0 +1,272 @@ +"use client" + +import * as React from "react" +import { + closestCenter, + DndContext, + KeyboardSensor, + MouseSensor, + TouchSensor, + useSensor, + useSensors, + type UniqueIdentifier, +} from "@dnd-kit/core" +import { restrictToVerticalAxis } from "@dnd-kit/modifiers" +import { + SortableContext, + verticalListSortingStrategy, +} from "@dnd-kit/sortable" +import { + IconChevronDown, + IconChevronLeft, + IconChevronRight, + IconChevronsLeft, + IconChevronsRight, + IconLayoutColumns, + IconPlus, +} from "@tabler/icons-react" +import { + ColumnFiltersState, + flexRender, + getCoreRowModel, + getFacetedRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getSortedRowModel, + SortingState, + useReactTable, + VisibilityState, +} from "@tanstack/react-table" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" +import { Label } from "@/components/ui/label" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@/components/ui/tabs" +import { schemaType } from "./schema" +import { getColumns, DraggableRow } from "./columns" +import { useRouter } from "next/navigation" + +export function LivingSpaceCompanyDataTable({ + data, + totalCount, + currentPage = 1, + pageSize = 10, + onPageChange, + onPageSizeChange, + refetchTable, + defaultSelection, + selectedCompanyIDS, + appendCompanyID, + removeCompanyID, + setDefaultSelection +}: { + data: schemaType[], + totalCount: number, + currentPage?: number, + pageSize?: number, + onPageChange: (page: number) => void, + onPageSizeChange: (size: number) => void, + refetchTable: () => void, + defaultSelection: string, + selectedCompanyIDS: string[], + appendCompanyID: (id: string) => void, + removeCompanyID: (id: string) => void, + setDefaultSelection: (id: string) => void +}) { + + const [rowSelection, setRowSelection] = React.useState({}) + const [columnVisibility, setColumnVisibility] = React.useState({}) + const [columnFilters, setColumnFilters] = React.useState([]) + const [sorting, setSorting] = React.useState([]) + const sortableId = React.useId() + const sensors = useSensors(useSensor(MouseSensor, {}), useSensor(TouchSensor, {}), useSensor(KeyboardSensor, {})) + const dataIds = React.useMemo(() => data?.map(({ _id }) => _id) || [], [data]) + + const columns = getColumns(appendCompanyID, removeCompanyID, selectedCompanyIDS, defaultSelection, setDefaultSelection); + const pagination = React.useMemo(() => ({ pageIndex: currentPage - 1, pageSize: pageSize }), [currentPage, pageSize]) + const totalPages = Math.ceil(totalCount / pageSize) + + const table = useReactTable({ + data, columns, pageCount: totalPages, + state: { sorting, columnVisibility, rowSelection, columnFilters, pagination }, + manualPagination: true, + getRowId: (row) => row._id.toString(), + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + onColumnVisibilityChange: setColumnVisibility, + onPaginationChange: (updater) => { const nextPagination = typeof updater === "function" ? updater(pagination) : updater; onPageChange(nextPagination.pageIndex + 1); onPageSizeChange(nextPagination.pageSize) }, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getSortedRowModel: getSortedRowModel(), + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + }) + + const handlePageSizeChange = (value: string) => { const newSize = Number(value); onPageSizeChange(newSize); onPageChange(1) } + + return ( + +
+ + +
+ + + + + + {table.getAllColumns().filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide()).map((column) => { + return ( + column.toggleVisibility(!!value)} > + {column.id} + + ) + })} + + +
+
+ +
+ + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + {table.getRowModel().rows.map((row) => )} + ) : ( + No results. + )} + +
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+
+ + +
+
+ Page {currentPage} of {totalPages} +
+
+ Total Count: {totalCount} +
+
+ + + + + + + +
+
+
+
+ +
+
+ +
+
+ +
+
+
+ ) +} \ No newline at end of file diff --git a/frontend/pages/users/selections/companies/page.tsx b/frontend/pages/users/selections/companies/page.tsx new file mode 100644 index 0000000..f4d8e40 --- /dev/null +++ b/frontend/pages/users/selections/companies/page.tsx @@ -0,0 +1,41 @@ +'use client'; +import { useState } from "react"; +import { useGraphQlCompanyList } from "./queries"; +import { LivingSpaceCompanyDataTable } from "./data-table"; + +const PageUsersCompanyTableSection = ( + { + selectedCompanyIDS, + appendCompanyID, + removeCompanyID, + defaultSelection, + setDefaultSelection + }: { + selectedCompanyIDS: string[], + appendCompanyID: (id: string) => void, + removeCompanyID: (id: string) => void, + defaultSelection: string, + setDefaultSelection: (id: string) => void + } +) => { + + const [page, setPage] = useState(1); + const [limit, setLimit] = useState(10); + const [sort, setSort] = useState({ createdAt: 'desc' }); + const [filters, setFilters] = useState({}); + const { data, isLoading, error, refetch } = useGraphQlCompanyList({ limit, skip: (page - 1) * limit, sort, filters }); + + const handlePageChange = (newPage: number) => { setPage(newPage) }; + const handlePageSizeChange = (newSize: number) => { setLimit(newSize); setPage(1) }; + + if (isLoading) { return
Loading...
} + if (error) { return
Error loading users
} + + return < LivingSpaceCompanyDataTable + data={data?.data || []} totalCount={data?.totalCount || 0} currentPage={page} pageSize={limit} onPageChange={handlePageChange} onPageSizeChange={handlePageSizeChange} + refetchTable={refetch} selectedCompanyIDS={selectedCompanyIDS} appendCompanyID={appendCompanyID} removeCompanyID={removeCompanyID} defaultSelection={defaultSelection} setDefaultSelection={setDefaultSelection} + /> + +} + +export default PageUsersCompanyTableSection; diff --git a/frontend/pages/users/selections/companies/queries.tsx b/frontend/pages/users/selections/companies/queries.tsx new file mode 100644 index 0000000..b1d5c88 --- /dev/null +++ b/frontend/pages/users/selections/companies/queries.tsx @@ -0,0 +1,19 @@ +'use client' +import { useQuery } from '@tanstack/react-query' +import { ListArguments } from '@/types/listRequest' +import { schemaType } from "./schema"; + +const fetchGraphQlCompanyList = async (params: ListArguments): Promise<{ data: schemaType[], totalCount: number }> => { + console.log('Fetching test data from local API'); + const { limit, skip, sort, filters } = params; + try { + const res = await fetch('/api/company/list', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ limit, skip, sort, filters }) }); + if (!res.ok) { const errorText = await res.text(); console.error('Test data API error:', errorText); throw new Error(`API error: ${res.status} ${res.statusText}`) } + const data = await res.json(); + return { data: data.data, totalCount: data.totalCount } + } catch (error) { console.error('Error fetching test data:', error); throw error } +}; + +export function useGraphQlCompanyList(params: ListArguments) { + return useQuery({ queryKey: ['graphql-company-list', params], queryFn: () => fetchGraphQlCompanyList(params) }) +} diff --git a/frontend/pages/users/selections/companies/schema.tsx b/frontend/pages/users/selections/companies/schema.tsx new file mode 100644 index 0000000..7b150ae --- /dev/null +++ b/frontend/pages/users/selections/companies/schema.tsx @@ -0,0 +1,27 @@ +import { z } from "zod"; + +export const schema = z.object({ + _id: z.string(), + uuid: z.string(), + formal_name: z.string(), + company_type: z.string(), + commercial_type: z.string(), + tax_no: z.string(), + public_name: z.string(), + company_tag: z.string(), + default_lang_type: z.string().default("TR"), + default_money_type: z.string().default("TL"), + is_commercial: z.boolean().default(false), + is_blacklist: z.boolean().default(false), + parent_id: z.string().optional(), + workplace_no: z.string().optional(), + official_address: z.string().optional(), + top_responsible_company: z.string().optional(), + expiryStarts: z.string().optional(), + expiryEnds: z.string().optional(), + createdAt: z.string().nullable().optional(), + updatedAt: z.string().nullable().optional(), +}); + + +export type schemaType = z.infer; \ No newline at end of file diff --git a/frontend/pages/users/update/form.tsx b/frontend/pages/users/update/form.tsx index 9c939e8..84d7833 100644 --- a/frontend/pages/users/update/form.tsx +++ b/frontend/pages/users/update/form.tsx @@ -1,4 +1,5 @@ "use client" +import { useState } from "react" import { useFieldArray, useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form" @@ -9,6 +10,7 @@ import { Separator } from "@/components/ui/separator" import { DateTimePicker } from "@/components/ui/date-time-picker" import { useUpdateUserMutation } from "@/pages/users/update/queries" import { userUpdateSchema, type UserUpdate } from "@/pages/users/update/schema" +import PageAddUserSelections from "../selections/addPage" const UserForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () => void, initData: UserUpdate, selectedUuid: string }) => { @@ -22,227 +24,175 @@ const UserForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () = tag: initData.tag, email: initData.email, phone: initData.phone, - collectionTokens: initData.collectionTokens, }, }) - const { control, handleSubmit } = form + const { handleSubmit } = form - const { fields, append, remove } = useFieldArray({ control, name: "collectionTokens.tokens" }) + const [defaultSelection, setDefaultSelection] = useState("") + const [selectedBuildIDS, setSelectedBuildIDS] = useState([]) + const [selectedCompanyIDS, setSelectedCompanyIDS] = useState([]) + + const appendBuildID = (id: string) => setSelectedBuildIDS((prev) => (id && !selectedBuildIDS.includes(id) ? [...prev, id] : prev)) + const appendCompanyID = (id: string) => setSelectedCompanyIDS((prev) => (id && !selectedCompanyIDS.includes(id) ? [...prev, id] : prev)) + + const removeBuildID = (id: string) => setSelectedBuildIDS((prev) => prev.filter((item) => item !== id)) + const removeCompanyID = (id: string) => setSelectedCompanyIDS((prev) => prev.filter((item) => item !== id)) const mutation = useUpdateUserMutation(); - - function onSubmit(values: UserUpdate) { mutation.mutate({ data: values as any || initData, uuid: selectedUuid }); setTimeout(() => refetchTable(), 400) } + function onSubmit(values: UserUpdate) { mutation.mutate({ data: values as any || initData, uuid: selectedUuid, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }); setTimeout(() => refetchTable(), 400) } return ( -
- - {/* BASIC INFO */} -
- ( - - Email - - - - - - )} - /> - - ( - - Phone - - - - - - )} - /> -
- - {/* PASSWORD / TAG */} -
- ( - - Tag - - - - - - )} - /> -
- - {/* DATES */} -
- - ( - - Expiry Starts - - - - - - )} - /> - - ( - - Expiry Ends - - - - - - )} - /> -
- - {/* SWITCHES */} -
- ( - - - - - Confirmed - - )} - /> - - ( - - - - - Send Notification - - )} - /> -
- - - - {/* COLLECTION TOKENS */} - ( - - Default Token - - - - - )} - /> - -
-
- Tokens - - +
+ + + + {/* BASIC INFO */} +
+ ( + + Email + + + + + + )} + /> + ( + + Phone + + + + + + )} + />
- {fields.length === 0 && ( -

No tokens added.

- )} + {/* PASSWORD / TAG */} +
+ ( + + Password + + + + + + )} + /> + ( + + Re-Password + + + + + + )} + /> - {fields.map((fieldItem, i) => ( -
-
- ( - - Prefix - - - - - - )} - /> -
- -
- ( - - Token - - - - - - )} - /> -
- - + ( + + Tag + + + + + + )} + /> + {/* SWITCHES */} +
+ ( + + + + + Confirmed + + )} + /> + ( + + + + + Send Notification + + )} + />
- ))} -
+
- + {/* DATES */} +
+ ( + + Expiry Starts + + + + + + )} + /> + ( + + Expiry Ends + + + + + + )} + /> +
+ + + + + +
- - - ) } diff --git a/frontend/pages/users/update/queries.tsx b/frontend/pages/users/update/queries.tsx index a6a2792..11ae905 100644 --- a/frontend/pages/users/update/queries.tsx +++ b/frontend/pages/users/update/queries.tsx @@ -2,10 +2,10 @@ import { useMutation } from '@tanstack/react-query' import { UserUpdate } from './types'; -const fetchGraphQlUsersUpdate = async (record: UserUpdate, uuid: string): Promise<{ data: UserUpdate | null; status: number }> => { +const fetchGraphQlUsersUpdate = async (record: UserUpdate, uuid: string, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, refetchTable: () => void): Promise<{ data: UserUpdate | null; status: number }> => { console.log('Fetching test data from local API'); try { - const res = await fetch(`/api/users/update?uuid=${uuid || ''}`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(record) }); + const res = await fetch(`/api/users/update?uuid=${uuid || ''}`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ ...record, selectedBuildIDS, selectedCompanyIDS, defaultSelection }) }); if (!res.ok) { const errorText = await res.text(); console.error('Test data API error:', errorText); throw new Error(`API error: ${res.status} ${res.statusText}`) } const data = await res.json(); return { data: data.data, status: res.status } @@ -14,7 +14,9 @@ const fetchGraphQlUsersUpdate = async (record: UserUpdate, uuid: string): Promis export function useUpdateUserMutation() { return useMutation({ - mutationFn: ({ data, uuid }: { data: UserUpdate, uuid: string }) => fetchGraphQlUsersUpdate(data, uuid), + mutationFn: ( + { data, uuid, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }: { data: UserUpdate, uuid: string, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, refetchTable: () => void } + ) => fetchGraphQlUsersUpdate(data, uuid, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable), onSuccess: () => { console.log("User updated successfully") }, onError: (error) => { console.error("Update user failed:", error) }, }) diff --git a/frontend/pages/users/update/schema.ts b/frontend/pages/users/update/schema.ts index d4ffa95..df7548a 100644 --- a/frontend/pages/users/update/schema.ts +++ b/frontend/pages/users/update/schema.ts @@ -1,14 +1,5 @@ import { z } from "zod" -export const tokenSchema = z.object({ - prefix: z.string().min(1, "Prefix is required"), - token: z.string().min(1, "Token is required"), -}) - -export const collectionTokensSchema = z.object({ - default: z.string().optional(), - tokens: z.array(tokenSchema) -}) export const userUpdateSchema = z.object({ expiryStarts: z.string().optional(), @@ -17,11 +8,11 @@ export const userUpdateSchema = z.object({ isConfirmed: z.boolean(), isNotificationSend: z.boolean(), + password: z.string().min(6), + rePassword: z.string().min(6), tag: z.string().optional(), email: z.string().email(), phone: z.string().min(5), - - collectionTokens: collectionTokensSchema, }) export type UserUpdate = z.infer