From 53e1f1e4fc301352fc0955858aa1ab04c728bd02 Mon Sep 17 00:00:00 2001 From: Berkay Date: Thu, 4 Dec 2025 15:46:24 +0300 Subject: [PATCH] updated living space added --- backend/src/build-iban/build-iban.module.ts | 6 +- backend/src/build-iban/build-iban.service.ts | 22 +- .../dto/create-build-ibans.input.ts | 3 + backend/src/lib/generateToken.ts | 8 + .../dto/create-living-space.input.ts | 4 +- .../dto/get-living-space.input.ts | 12 + .../src/living-space/living-space.module.ts | 2 + .../src/living-space/living-space.resolver.ts | 4 + .../src/living-space/living-space.service.ts | 28 +- backend/src/models/build-parts.model.ts | 22 +- backend/src/models/build.model.ts | 7 +- backend/src/models/living-spaces.model.ts | 15 +- backend/src/models/user.model.ts | 9 +- backend/src/users/dto/create-user.input.ts | 7 +- backend/src/users/users.queries.graphql | 13 +- frontend/app/api/build-ibans/add/route.ts | 5 +- frontend/app/api/living-space/detail/route.ts | 37 +++ .../app/api/living-space/list/oldroute.ts | 43 +++ frontend/app/api/living-space/list/route.ts | 44 ++- frontend/app/api/users/add/route.ts | 24 +- frontend/app/api/users/add/schema.ts | 22 +- frontend/app/api/users/list/route.ts | 11 +- frontend/app/api/users/update/schema.ts | 30 +- frontend/components/sidebar/app-sidebar.tsx | 10 +- frontend/pages/build-ibans/add/form.tsx | 25 +- frontend/pages/build-ibans/add/page.tsx | 19 +- frontend/pages/build-ibans/add/queries.tsx | 8 +- .../build-ibans/add/table/data-table.tsx | 9 +- .../pages/build-ibans/list/data-table.tsx | 6 +- frontend/pages/build-ibans/page.tsx | 15 +- frontend/pages/build-ibans/update/form.tsx | 5 +- frontend/pages/build-ibans/update/page.tsx | 5 +- frontend/pages/build-ibans/update/queries.tsx | 9 +- .../build-ibans/update/table/data-table.tsx | 6 +- frontend/pages/builds/list/data-table.tsx | 7 +- frontend/pages/living-space/add/page.tsx | 3 +- frontend/pages/living-space/list/page.tsx | 5 +- frontend/pages/living-space/list/queries.tsx | 13 + .../tables/living-spaces/list/columns.tsx | 59 +++- .../tables/living-spaces/list/data-table.tsx | 4 +- .../tables/living-spaces/list/queries.tsx | 1 - frontend/pages/living-space/update/page.tsx | 13 +- frontend/pages/users/add/form.tsx | 13 +- frontend/pages/users/add/page.tsx | 6 +- frontend/pages/users/add/queries.tsx | 21 +- frontend/pages/users/add/schema.ts | 8 +- frontend/pages/users/add/table/columns.tsx | 181 ++--------- frontend/pages/users/add/table/data-table.tsx | 2 +- frontend/pages/users/add/table/schema.tsx | 57 ++-- frontend/pages/users/add/types.ts | 4 +- frontend/pages/users/page.tsx | 16 +- frontend/pages/users/selections/addPage.tsx | 9 +- .../pages/users/selections/builds/columns.tsx | 33 +- .../pages/users/selections/builds/page.tsx | 2 - .../pages/users/selections/people/columns.tsx | 148 +++++++++ .../users/selections/people/data-table.tsx | 282 ++++++++++++++++++ .../pages/users/selections/people/page.tsx | 30 ++ .../pages/users/selections/people/queries.tsx | 37 +++ .../pages/users/selections/people/schema.tsx | 27 ++ frontend/pages/users/table/columns.tsx | 180 ++--------- frontend/pages/users/table/data-table.tsx | 2 +- frontend/pages/users/table/schema.tsx | 57 ++-- frontend/pages/users/types.ts | 20 +- frontend/pages/users/update/form.tsx | 43 +-- frontend/pages/users/update/page.tsx | 14 +- frontend/pages/users/update/queries.tsx | 16 +- frontend/pages/users/update/schema.ts | 4 +- frontend/pages/users/update/table/columns.tsx | 81 +++-- .../pages/users/update/table/data-table.tsx | 2 +- frontend/pages/users/update/table/schema.tsx | 57 ++-- 70 files changed, 1128 insertions(+), 824 deletions(-) create mode 100644 backend/src/lib/generateToken.ts create mode 100644 backend/src/living-space/dto/get-living-space.input.ts create mode 100644 frontend/app/api/living-space/detail/route.ts create mode 100644 frontend/app/api/living-space/list/oldroute.ts create mode 100644 frontend/pages/users/selections/people/columns.tsx create mode 100644 frontend/pages/users/selections/people/data-table.tsx create mode 100644 frontend/pages/users/selections/people/page.tsx create mode 100644 frontend/pages/users/selections/people/queries.tsx create mode 100644 frontend/pages/users/selections/people/schema.tsx diff --git a/backend/src/build-iban/build-iban.module.ts b/backend/src/build-iban/build-iban.module.ts index 5c86ddb..3b9b036 100644 --- a/backend/src/build-iban/build-iban.module.ts +++ b/backend/src/build-iban/build-iban.module.ts @@ -2,10 +2,12 @@ import { Module } from '@nestjs/common'; import { BuildIbanResolver } from './build-iban.resolver'; import { BuildIbanService } from './build-iban.service'; import { MongooseModule } from '@nestjs/mongoose'; -import { BuildIban, BuildIbanSchema } from '@/models/build.model'; +import { BuildIban, BuildIbanSchema, Build, BuildSchema } from '@/models/build.model'; @Module({ - imports: [MongooseModule.forFeature([{ name: BuildIban.name, schema: BuildIbanSchema }])], + imports: [MongooseModule.forFeature([ + { name: BuildIban.name, schema: BuildIbanSchema }, { name: Build.name, schema: BuildSchema } + ])], providers: [BuildIbanResolver, BuildIbanService] }) export class BuildIbanModule { } diff --git a/backend/src/build-iban/build-iban.service.ts b/backend/src/build-iban/build-iban.service.ts index 08804f2..42f833a 100644 --- a/backend/src/build-iban/build-iban.service.ts +++ b/backend/src/build-iban/build-iban.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Types, Model } from 'mongoose'; -import { BuildIban, BuildIbanDocument } from '@/models/build.model'; +import { Build, BuildDocument, BuildIban, BuildIbanDocument } from '@/models/build.model'; import { ListBuildIbanResponse } from './dto/list-build-ibans.response'; import { CreateBuildIbanInput } from './dto/create-build-ibans.input'; import { UpdateBuildIbanInput } from './dto/update-build-ibans.input'; @@ -9,10 +9,14 @@ import { UpdateBuildIbanInput } from './dto/update-build-ibans.input'; @Injectable() export class BuildIbanService { - constructor(@InjectModel(BuildIban.name) private readonly buildIbanModel: Model) { } + constructor( + @InjectModel(BuildIban.name) private readonly buildIbanModel: Model, + @InjectModel(Build.name) private readonly buildModel: Model + ) { } async findAll(projection: any, skip: number, limit: number, sort?: Record, filters?: Record): Promise { const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) }; + if (!!query?.buildId) { query.buildId = new Types.ObjectId(query?.buildId) }; const totalCount = await this.buildIbanModel.countDocuments(query).exec(); const data = await this.buildIbanModel.find(query, projection, { lean: true }).skip(skip).limit(limit).sort(sort).exec(); return { data, totalCount }; @@ -22,17 +26,21 @@ export class BuildIbanService { return this.buildIbanModel.findById(id, projection, { lean: false }).populate({ path: 'buildIban', select: projection?.buildIban }).exec(); } - async create(input: CreateBuildIbanInput): Promise { const buildIban = new this.buildIbanModel(input); return buildIban.save() } + async create(input: CreateBuildIbanInput): Promise { + const build = await this.buildModel.findOne({ _id: new Types.ObjectId(input.buildId) }); + if (!build) { throw new Error('Build not found') } + const buildIban = new this.buildIbanModel({ ...input, buildId: build._id }); return buildIban.save() + } - async update(uuid: string, input: UpdateBuildIbanInput): Promise { const buildIban = await this.buildIbanModel.findOne({ uuid }); if (!buildIban) { throw new Error('BuildIban not found') }; buildIban.set(input); return buildIban.save() } + async update(uuid: string, input: UpdateBuildIbanInput): Promise { + const buildIban = await this.buildIbanModel.findOne({ uuid }); if (!buildIban) { throw new Error('BuildIban not found') }; buildIban.set(input); return buildIban.save() + } async delete(uuid: string): Promise { const buildIban = await this.buildIbanModel.deleteMany({ uuid }); return buildIban.deletedCount > 0 } buildProjection(fields: Record): any { const projection: any = {}; - for (const key in fields) { - if (key === 'buildIban' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`buildIban.${subField}`] = 1 } } else { projection[key] = 1 } - }; return projection; + for (const key in fields) { if (key === 'buildIban' && typeof fields[key] === 'object') { for (const subField of Object.keys(fields[key])) { projection[`buildIban.${subField}`] = 1 } } else { projection[key] = 1 } }; return projection; } } diff --git a/backend/src/build-iban/dto/create-build-ibans.input.ts b/backend/src/build-iban/dto/create-build-ibans.input.ts index d10c261..d751b64 100644 --- a/backend/src/build-iban/dto/create-build-ibans.input.ts +++ b/backend/src/build-iban/dto/create-build-ibans.input.ts @@ -4,6 +4,9 @@ import { InputType, Field } from "@nestjs/graphql"; @InputType() export class CreateBuildIbanInput extends ExpiryBaseInput { + @Field() + buildId: string; + @Field() iban: string; diff --git a/backend/src/lib/generateToken.ts b/backend/src/lib/generateToken.ts new file mode 100644 index 0000000..48c27cf --- /dev/null +++ b/backend/src/lib/generateToken.ts @@ -0,0 +1,8 @@ +import { randomBytes, createHash } from "crypto"; + +export function generateResetToken(length: number = 128) { + const resetToken = randomBytes(length).toString("hex"); + const hashedToken = createHash("sha256").update(resetToken).digest("hex"); + const expires = Date.now() + 1000 * 60 * 60 * 24 * 3; + return { resetToken, hashedToken, expires }; +} diff --git a/backend/src/living-space/dto/create-living-space.input.ts b/backend/src/living-space/dto/create-living-space.input.ts index 511cecb..ea0ea54 100644 --- a/backend/src/living-space/dto/create-living-space.input.ts +++ b/backend/src/living-space/dto/create-living-space.input.ts @@ -13,8 +13,8 @@ export class CreateLivingSpaceInput extends ExpiryBaseInput { @Field() userTypeID: string; - @Field() - companyID: string; + @Field({ nullable: true }) + companyID?: string; @Field() personID: string; diff --git a/backend/src/living-space/dto/get-living-space.input.ts b/backend/src/living-space/dto/get-living-space.input.ts new file mode 100644 index 0000000..e11a3bb --- /dev/null +++ b/backend/src/living-space/dto/get-living-space.input.ts @@ -0,0 +1,12 @@ +import { InputType, Field } from '@nestjs/graphql'; + +@InputType() +export class GetLivingSpaceInput { + + @Field() + buildID: string; + + @Field() + uuid: string; + +} diff --git a/backend/src/living-space/living-space.module.ts b/backend/src/living-space/living-space.module.ts index a30180b..a2845f5 100644 --- a/backend/src/living-space/living-space.module.ts +++ b/backend/src/living-space/living-space.module.ts @@ -4,11 +4,13 @@ import { LivingSpaceResolver } from './living-space.resolver'; import { MongooseModule } from '@nestjs/mongoose'; import { LivingSpaces, LivingSpacesSchema } from '@/models/living-spaces.model'; import { UserType, UserTypeSchema } from '@/models/user-type.model'; +import { Build, BuildSchema } from '@/models/build.model'; @Module({ imports: [MongooseModule.forFeature([ { name: LivingSpaces.name, schema: LivingSpacesSchema }, { name: UserType.name, schema: UserTypeSchema }, + { name: Build.name, schema: BuildSchema }, ])], providers: [LivingSpaceService, LivingSpaceResolver] }) diff --git a/backend/src/living-space/living-space.resolver.ts b/backend/src/living-space/living-space.resolver.ts index 4263d42..6bf476c 100644 --- a/backend/src/living-space/living-space.resolver.ts +++ b/backend/src/living-space/living-space.resolver.ts @@ -8,6 +8,7 @@ import { UpdateLivingSpaceInput } from './dto/update-living-space.input'; import { LivingSpaceService } from './living-space.service'; import type { GraphQLResolveInfo } from 'graphql'; import graphqlFields from 'graphql-fields'; +import { GetLivingSpaceInput } from './dto/get-living-space.input'; @Resolver() export class LivingSpaceResolver { @@ -25,6 +26,9 @@ export class LivingSpaceResolver { const fields = graphqlFields(info); const projection = this.livingSpaceService.buildProjection(fields); return this.livingSpaceService.findById(buildID, new Types.ObjectId(id), projection); } + @Query(() => LivingSpaces, { name: 'getLivingSpaceDetail', nullable: true }) + async getLivingSpaceDetail(@Args('uuid') uuid: string, @Args('buildID') buildID: string): Promise { return this.livingSpaceService.getDetail(buildID, uuid) } + @Mutation(() => LivingSpaces, { name: 'createLivingSpace' }) async createLivingSpace(@Args('input') input: CreateLivingSpaceInput): Promise { return this.livingSpaceService.create(input) } diff --git a/backend/src/living-space/living-space.service.ts b/backend/src/living-space/living-space.service.ts index cd520b8..6382e48 100644 --- a/backend/src/living-space/living-space.service.ts +++ b/backend/src/living-space/living-space.service.ts @@ -6,6 +6,7 @@ import { CreateLivingSpaceInput } from './dto/create-living-space.input'; import { UpdateLivingSpaceInput } from './dto/update-living-space.input'; import { InjectConnection, InjectModel } from '@nestjs/mongoose'; import { UserTypeDocument } from '@/models/user-type.model'; +import { BuildDocument } from '@/models/build.model'; interface UpdateInput { userType?: Types.ObjectId; @@ -21,17 +22,30 @@ export class LivingSpaceService { constructor( @InjectConnection() private readonly connection: Connection, - @InjectModel('UserType') private readonly userTypeModel: Model + @InjectModel('UserType') private readonly userTypeModel: Model, + @InjectModel('Build') private readonly buildModel: Model ) { } async findAll(buildID: string, projection: any, skip: number, limit: number, sort?: Record, filters?: Record): Promise { const selectedModel = getDynamicLivingSpaceModel(buildID, this.connection); const query: any = {}; if (filters && Object.keys(filters).length > 0) { Object.assign(query, filters) }; const totalCount = await selectedModel.countDocuments(query).exec(); - const data = await selectedModel.find(query, projection, { lean: true }).skip(skip).limit(limit).sort(sort).exec(); + const data = await selectedModel.find(query, projection, { lean: false }) + .populate({ path: 'build', select: { ...projection?.build, _id: 0 } }) + .populate({ path: 'person', select: { ...projection?.person, _id: 0 } }) + .populate({ path: 'company', select: { ...projection?.company, _id: 0 } }) + .populate({ path: 'userType', select: { ...projection?.userType, _id: 0 } }) + .populate({ path: 'part', select: { ...projection?.part, _id: 0 } }) + .skip(skip).limit(limit).sort(sort).exec(); return { data, totalCount }; } + async getDetail(buildID: string, uuid: string): Promise { + const buildObjectID = new Types.ObjectId(buildID); const build = await this.buildModel.findById(buildObjectID); if (!build) { throw new Error('Build not found') }; + const selectedModel = getDynamicLivingSpaceModel(buildID, this.connection); const data = await selectedModel.findOne({ uuid }, { lean: false }).exec(); + if (!data) { throw new Error('Living space not found') }; return data; + } + async findById(buildID: string, id: Types.ObjectId, projection?: any): Promise { const selectedModel = getDynamicLivingSpaceModel(buildID, this.connection); return selectedModel.findById(id, projection, { lean: false }).populate({ path: 'company', select: projection?.company }).exec(); @@ -42,7 +56,7 @@ export class LivingSpaceService { const LivingSpaceModel = getDynamicLivingSpaceModel(input.buildID, this.connection); const docInput: Partial = { build: new Types.ObjectId(input.buildID), part: new Types.ObjectId(input.partID), userType: new Types.ObjectId(input.userTypeID), - company: new Types.ObjectId(input.companyID), person: new Types.ObjectId(input.personID), + company: !!input.companyID ? new Types.ObjectId(input.companyID) : undefined, person: new Types.ObjectId(input.personID), expiryStarts: input.expiryStarts ? new Date(input.expiryStarts) : new Date(), expiryEnds: input.expiryEnds ? new Date(input.expiryEnds) : new Date('2099-12-31'), }; const doc = new LivingSpaceModel(docInput); return await doc.save() } @@ -51,10 +65,10 @@ export class LivingSpaceService { if (!buildID) { throw new Error('Build ID is required') } const selectedModel = getDynamicLivingSpaceModel(buildID, this.connection); const livingSpace = await selectedModel.findOne({ uuid }); const userTypeSelected = await this.userTypeModel.findById(new Types.ObjectId(input.userTypeID)).exec(); const newInput: UpdateInput = {} - if (userTypeSelected?.isProperty) { if (input?.partID) { newInput.part = new Types.ObjectId(input.partID) } } else { newInput.part = null } - if (input?.companyID) { newInput.company = new Types.ObjectId(input.companyID) } - if (input?.personID) { newInput.person = new Types.ObjectId(input.personID) }; if (input?.userTypeID) { newInput.userType = new Types.ObjectId(input.userTypeID) } - if (input?.expiryStarts) { newInput.expiryStarts = input.expiryStarts }; if (input?.expiryEnds) { newInput.expiryEnds = input.expiryEnds } + if (userTypeSelected?.isProperty) { if (!!input?.partID) { newInput.part = new Types.ObjectId(input.partID) } } else { newInput.part = null } + if (!!input?.companyID) { newInput.company = new Types.ObjectId(input.companyID) } + if (!!input?.personID) { newInput.person = new Types.ObjectId(input.personID) }; if (!!input?.userTypeID) { newInput.userType = new Types.ObjectId(input.userTypeID) } + if (!!input?.expiryStarts) { newInput.expiryStarts = input.expiryStarts }; if (!!input?.expiryEnds) { newInput.expiryEnds = input.expiryEnds } if (!livingSpace) { throw new Error('Company not found') }; livingSpace.set(newInput); return livingSpace.save(); } diff --git a/backend/src/models/build-parts.model.ts b/backend/src/models/build-parts.model.ts index 4eb9a4d..dd3e3bb 100644 --- a/backend/src/models/build-parts.model.ts +++ b/backend/src/models/build-parts.model.ts @@ -10,9 +10,17 @@ export class BuildParts extends Base { @Field(() => ID) readonly _id: string; - @Field(() => ID) - @Prop({ type: Types.ObjectId, ref: 'Build', required: true }) - buildId: Types.ObjectId; + @Field(() => ID, { nullable: true }) + @Prop({ type: Types.ObjectId, ref: 'Build', required: false }) + buildId?: Types.ObjectId; + + @Field(() => ID, { nullable: true }) + @Prop({ type: Types.ObjectId, ref: 'ApiEnumDropdown', required: false }) + directionId?: Types.ObjectId; + + @Field(() => ID, { nullable: true }) + @Prop({ type: Types.ObjectId, ref: 'ApiEnumDropdown', required: false }) + typeId?: Types.ObjectId; @Field() @Prop({ required: true }) @@ -50,14 +58,6 @@ export class BuildParts extends Base { @Prop({ required: true }) key: string; - @Field(() => ID, { nullable: true }) - @Prop({ type: Types.ObjectId, ref: 'ApiEnumDropdown', required: false }) - directionId: Types.ObjectId; - - @Field(() => ID, { nullable: true }) - @Prop({ type: Types.ObjectId, ref: 'ApiEnumDropdown', required: false }) - typeId: Types.ObjectId; - } export type BuildPartsDocument = BuildParts & Document; diff --git a/backend/src/models/build.model.ts b/backend/src/models/build.model.ts index 6c51672..438cc79 100644 --- a/backend/src/models/build.model.ts +++ b/backend/src/models/build.model.ts @@ -2,8 +2,6 @@ import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document, Types } from 'mongoose'; import { ObjectType, Field, ID, Int } from '@nestjs/graphql'; import { Base, CreatedBase } from '@/models/base.model'; -import { Person } from '@/models/person.model'; -import { Company } from '@/models/company.model'; import { BuildTypes } from './build-types.model'; @ObjectType() @@ -13,6 +11,10 @@ export class BuildIban extends Base { @Field(() => ID) readonly _id: string; + @Field(() => ID, { nullable: true }) + @Prop({ type: Types.ObjectId, ref: 'Build', required: false }) + buildId?: Types.ObjectId; + @Field() @Prop({ required: true }) iban: string; @@ -144,6 +146,7 @@ export class Build extends CreatedBase { @Prop({ type: [String], default: [] }) areas?: string[]; + // collect String(ObjectID) to @Field(() => [String], { nullable: true, defaultValue: [] }) @Prop({ type: [String], default: [] }) ibans?: string[]; diff --git a/backend/src/models/living-spaces.model.ts b/backend/src/models/living-spaces.model.ts index 9e54b91..f0a33d2 100644 --- a/backend/src/models/living-spaces.model.ts +++ b/backend/src/models/living-spaces.model.ts @@ -8,7 +8,6 @@ import { Person } from '@/models/person.model'; import { Company } from '@/models/company.model'; import { UserType } from '@/models/user-type.model'; - @ObjectType() @Schema({ timestamps: true }) export class LivingSpaces extends Base { @@ -16,23 +15,23 @@ export class LivingSpaces extends Base { @Field(() => ID) readonly _id: string - @Field(() => ID) + @Field(() => Build) @Prop({ type: Types.ObjectId, ref: Build.name, required: true }) build: Types.ObjectId; - @Field(() => ID, { nullable: true }) + @Field(() => BuildParts, { nullable: true }) @Prop({ type: Types.ObjectId, ref: BuildParts.name, required: false }) part?: Types.ObjectId | null; - @Field(() => ID) + @Field(() => UserType) @Prop({ type: Types.ObjectId, ref: UserType.name, required: true }) userType: Types.ObjectId; - @Field(() => ID) - @Prop({ type: Types.ObjectId, ref: Company.name, required: true }) - company: Types.ObjectId; + @Field(() => Company, { nullable: true }) + @Prop({ type: Types.ObjectId, ref: Company.name, required: false }) + company?: Types.ObjectId; - @Field(() => ID) + @Field(() => Person) @Prop({ type: Types.ObjectId, ref: Person.name, required: true }) person: Types.ObjectId; diff --git a/backend/src/models/user.model.ts b/backend/src/models/user.model.ts index aaa4c82..663d5e4 100644 --- a/backend/src/models/user.model.ts +++ b/backend/src/models/user.model.ts @@ -3,6 +3,7 @@ import { ObjectType, Field, ID } from '@nestjs/graphql'; import { Document, Types } from 'mongoose'; import { Base } from '@/models/base.model'; import { Person } from '@/models/person.model'; +import { generateResetToken } from '@/lib/generateToken'; @ObjectType() export class CollectionToken { @@ -35,12 +36,12 @@ export class User extends Base { expiresAt?: Date; @Field({ nullable: true }) - @Prop() + @Prop({ required: false, default: () => generateResetToken().hashedToken }) resetToken?: string; - @Field() - @Prop({ required: true }) - password: string; + @Field({ nullable: true }) + @Prop({ required: false, default: '' }) + password?: string; @Field(() => [String], { nullable: 'itemsAndList' }) @Prop({ type: [String], default: [], validate: [(val: string[]) => val.length <= 3, 'History can have max 3 items'] }) diff --git a/backend/src/users/dto/create-user.input.ts b/backend/src/users/dto/create-user.input.ts index 75feb72..9a99ae4 100644 --- a/backend/src/users/dto/create-user.input.ts +++ b/backend/src/users/dto/create-user.input.ts @@ -20,11 +20,8 @@ export class CreateUserInput { @Field({ nullable: true }) avatar?: string; - @Field() - password: string; - - @Field(() => [String], { nullable: true }) - history?: string[]; + // @Field() + // password: string; @Field() tag: string; diff --git a/backend/src/users/users.queries.graphql b/backend/src/users/users.queries.graphql index a554f63..3385039 100644 --- a/backend/src/users/users.queries.graphql +++ b/backend/src/users/users.queries.graphql @@ -6,8 +6,9 @@ mutation { email: "test@example.com", phone: "555123456", collectionTokens: { - default: "default-token", - tokens: [{ prefix: "main", token: "abc123" }] + defaultSelection: "default-token", + selectedBuildIDS: ["64f8b2a4e1234567890abcdef"], + selectedCompanyIDS: ["64f8b2a4e1234567890abcdef"] }, person: "64f8b2a4e1234567890abcdef" }) { @@ -18,11 +19,9 @@ mutation { phone tag collectionTokens { - default - tokens { - prefix - token - } + defaultSelection + selectedBuildIDS + selectedCompanyIDS } person } diff --git a/frontend/app/api/build-ibans/add/route.ts b/frontend/app/api/build-ibans/add/route.ts index ae7147b..d6f4508 100644 --- a/frontend/app/api/build-ibans/add/route.ts +++ b/frontend/app/api/build-ibans/add/route.ts @@ -8,6 +8,9 @@ const endpoint = "http://localhost:3001/graphql"; export async function POST(request: Request) { const body = await request.json(); const validatedBody = buildIbansAddSchema.parse(body); + const url = new URL(request.url) + const buildID = url.searchParams.get('build'); + if (!buildID) { return NextResponse.json({ error: 'Build ID is required' }, { status: 400 }) } try { const client = new GraphQLClient(endpoint); const query = gql` @@ -22,7 +25,7 @@ export async function POST(request: Request) { } } `; - const variables = { input: validatedBody }; + const variables = { input: { ...validatedBody, buildId: buildID } }; const data = await client.request(query, variables); return NextResponse.json({ data: data.createBuildIban, status: 200 }); } catch (err: any) { diff --git a/frontend/app/api/living-space/detail/route.ts b/frontend/app/api/living-space/detail/route.ts new file mode 100644 index 0000000..b10ef4a --- /dev/null +++ b/frontend/app/api/living-space/detail/route.ts @@ -0,0 +1,37 @@ +'use server'; +import { NextResponse } from 'next/server'; +import { GraphQLClient, gql } from 'graphql-request'; + +const endpoint = "http://localhost:3001/graphql"; + +export async function POST(request: Request) { + const body = await request.json(); + const { uuid, buildID } = body; + try { + const client = new GraphQLClient(endpoint); + const query = gql` + query GetLivingSpaceDetail($uuid: String!, $buildID: String!) { + getLivingSpaceDetail(uuid: $uuid, buildID: $buildID) { + _id + company {_id} + userType {_id} + build {_id} + person {_id} + part {_id} + } + } + `; + const variables = { uuid, buildID }; + const data = await client.request(query, variables); + if (!data?.getLivingSpaceDetail) { return NextResponse.json({ error: 'Living space not found' }, { status: 404 }) }; + return NextResponse.json({ + data: { + _id: data.getLivingSpaceDetail._id, company: data.getLivingSpaceDetail?.company?._id, userType: data.getLivingSpaceDetail.userType._id, + build: data.getLivingSpaceDetail.build._id, person: data.getLivingSpaceDetail.person._id, part: data.getLivingSpaceDetail.part._id, + } + }); + } catch (err: any) { + console.error(err); + return NextResponse.json({ error: err.message }, { status: 500 }); + } +} diff --git a/frontend/app/api/living-space/list/oldroute.ts b/frontend/app/api/living-space/list/oldroute.ts new file mode 100644 index 0000000..9163bd8 --- /dev/null +++ b/frontend/app/api/living-space/list/oldroute.ts @@ -0,0 +1,43 @@ +'use server'; +import { NextResponse } from 'next/server'; +import { GraphQLClient, gql } from 'graphql-request'; + +const endpoint = "http://localhost:3001/graphql"; + +export async function POST(request: Request) { + const body = await request.json(); + const { limit, skip, sort, filters, buildID } = body; + try { + const client = new GraphQLClient(endpoint); + const query = gql` + query GetLivingSpaces($input: ListArguments!, $buildID: String!) { + getLivingSpaces(input: $input, buildID: $buildID) { + data { + _id + uuid + build + part + userType + company + person + isNotificationSend + expiryEnds + expiryStarts + createdAt + updatedAt + isConfirmed + deleted + + } + totalCount + } + } + `; + const variables = { input: { limit, skip, sort, filters }, buildID }; + const data = await client.request(query, variables); + return NextResponse.json({ data: data.getLivingSpaces.data, totalCount: data.getLivingSpaces.totalCount }); + } catch (err: any) { + console.error(err); + return NextResponse.json({ error: err.message }, { status: 500 }); + } +} diff --git a/frontend/app/api/living-space/list/route.ts b/frontend/app/api/living-space/list/route.ts index 9163bd8..23b0ec9 100644 --- a/frontend/app/api/living-space/list/route.ts +++ b/frontend/app/api/living-space/list/route.ts @@ -10,26 +10,42 @@ export async function POST(request: Request) { try { const client = new GraphQLClient(endpoint); const query = gql` - query GetLivingSpaces($input: ListArguments!, $buildID: String!) { - getLivingSpaces(input: $input, buildID: $buildID) { + query GetLivingSpaces($buildID: String!, $input: ListArguments!) { + getLivingSpaces(buildID: $buildID, input: $input) { data { _id uuid - build - part - userType - company - person - isNotificationSend - expiryEnds - expiryStarts createdAt updatedAt - isConfirmed - deleted - + expiryStarts + expiryEnds + build { + info { + buildName + buildNo + } + } + part { + no + level + humanLivability + } + person { + firstName + middleName + surname + birthDate + birthPlace + } + userType { + isProperty + description + } + company { + uuid + } } - totalCount + totalCount } } `; diff --git a/frontend/app/api/users/add/route.ts b/frontend/app/api/users/add/route.ts index 2907273..2285c1b 100644 --- a/frontend/app/api/users/add/route.ts +++ b/frontend/app/api/users/add/route.ts @@ -10,25 +10,23 @@ export async function POST(request: Request) { console.log("BODY") console.dir({ body }) const validatedBody = userAddSchema.parse(body); - validatedBody.person = "6917732face2287b1d901738" try { const client = new GraphQLClient(endpoint); const query = gql` mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { - _id - password - tag - email - phone - collectionTokens { - default - tokens { - prefix - token + _id + password + tag + email + phone + tag + collectionTokens { + defaultSelection + selectedBuildIDS + selectedCompanyIDS } - } - person + person } } `; diff --git a/frontend/app/api/users/add/schema.ts b/frontend/app/api/users/add/schema.ts index cb1e0b1..c868b23 100644 --- a/frontend/app/api/users/add/schema.ts +++ b/frontend/app/api/users/add/schema.ts @@ -1,29 +1,21 @@ 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(), expiryEnds: z.string().optional(), - isConfirmed: z.boolean(), isNotificationSend: z.boolean(), - - password: z.string().min(6), tag: z.string().optional(), email: z.string().email(), phone: z.string().min(5), - person: z.string().optional(), + person: z.string(), + collectionTokens: z.object({ + defaultSelection: z.string(), + selectedBuildIDS: z.array(z.string()), + selectedCompanyIDS: z.array(z.string()), + }).optional() - collectionTokens: collectionTokensSchema, }) export type UserAdd = z.infer diff --git a/frontend/app/api/users/list/route.ts b/frontend/app/api/users/list/route.ts index 856ae10..fb8ea87 100644 --- a/frontend/app/api/users/list/route.ts +++ b/frontend/app/api/users/list/route.ts @@ -36,17 +36,16 @@ export async function POST(request: Request) { tag email phone + person collectionTokens { - default - tokens { - prefix - token - } + defaultSelection + selectedBuildIDS + selectedCompanyIDS } createdAt updatedAt } - totalCount + totalCount } } `; diff --git a/frontend/app/api/users/update/schema.ts b/frontend/app/api/users/update/schema.ts index de68b37..76c5987 100644 --- a/frontend/app/api/users/update/schema.ts +++ b/frontend/app/api/users/update/schema.ts @@ -1,28 +1,20 @@ import { z } from "zod" -export const tokenSchema = z.object({ - prefix: z.string().min(1, "Prefix is required").optional(), - token: z.string().min(1, "Token is required").optional(), -}) - -export const collectionTokensSchema = z.object({ - default: z.string().optional(), - tokens: z.array(tokenSchema).optional() -}) - export const userUpdateSchema = z.object({ + expiryStarts: z.string().optional(), expiryEnds: z.string().optional(), - - isConfirmed: z.boolean().optional(), - isNotificationSend: z.boolean().optional(), - + isConfirmed: z.boolean(), + isNotificationSend: z.boolean(), tag: z.string().optional(), - email: z.string().email().optional(), - phone: z.string().min(5).optional(), - person: z.string().optional(), - - collectionTokens: collectionTokensSchema, + email: z.string().email(), + phone: z.string().min(5), + person: z.string(), + collectionTokens: z.object({ + defaultSelection: z.string(), + selectedBuildIDS: z.array(z.string()), + selectedCompanyIDS: z.array(z.string()), + }).optional() }) export type UserUpdate = z.infer diff --git a/frontend/components/sidebar/app-sidebar.tsx b/frontend/components/sidebar/app-sidebar.tsx index e41e774..983db9e 100644 --- a/frontend/components/sidebar/app-sidebar.tsx +++ b/frontend/components/sidebar/app-sidebar.tsx @@ -62,11 +62,11 @@ const data = { url: "/build-address", icon: IconAddressBook }, - { - title: "Build Sites", - url: "/build-sites", - icon: IconMessageCircle - }, + // { + // title: "Build Sites", + // url: "/build-sites", + // icon: IconMessageCircle + // }, { title: "Build Areas", url: "/build-areas", diff --git a/frontend/pages/build-ibans/add/form.tsx b/frontend/pages/build-ibans/add/form.tsx index 2ec39a2..0d97d80 100644 --- a/frontend/pages/build-ibans/add/form.tsx +++ b/frontend/pages/build-ibans/add/form.tsx @@ -9,35 +9,20 @@ import { DateTimePicker } from "@/components/ui/date-time-picker" import { BuildIbansAdd, buildIbansAddSchema } from "./schema" import { useAddBuildIbansMutation } from "./queries" -const BuildIbansForm = ({ refetchTable }: { refetchTable: () => void }) => { +const BuildIbansForm = ({ refetchTable, buildId }: { refetchTable: () => void, buildId: string }) => { const form = useForm({ - resolver: zodResolver(buildIbansAddSchema), - defaultValues: { - iban: "", - startDate: "", - stopDate: "", - bankCode: "", - xcomment: "", - expiryStarts: "", - expiryEnds: "", - }, + resolver: zodResolver(buildIbansAddSchema), defaultValues: { iban: "", startDate: "", stopDate: "", bankCode: "", xcomment: "", expiryStarts: "", expiryEnds: "" }, }); - const { handleSubmit } = form; - const mutation = useAddBuildIbansMutation(); - - function onSubmit(values: BuildIbansAdd) { mutation.mutate({ data: values }); setTimeout(() => refetchTable(), 400) }; + function onSubmit(values: BuildIbansAdd) { mutation.mutate({ data: values, buildId, refetchTable }) }; return (
- - {/* ROW 1 */} + + {/* ROW 1 */}
{ const [page, setPage] = useState(1); @@ -10,14 +12,19 @@ const PageAddBuildIbans = () => { const [sort, setSort] = useState({ createdAt: 'desc' }); const [filters, setFilters] = useState({}); - const { data, isLoading, error, refetch } = useGraphQlBuildIbansList({ limit, skip: (page - 1) * limit, sort, filters }); - + const searchParams = useSearchParams(); + const router = useRouter(); + const buildId = searchParams?.get('build'); + const { data, isLoading, error, refetch } = useGraphQlBuildIbansList({ limit, skip: (page - 1) * limit, sort, filters: { ...filters, buildId } }); + const noUUIDFound = <> +
Back To Build IBANs. No uuid is found on headers
+ + + if (!buildId) { return noUUIDFound } return ( <> - - + + ) } diff --git a/frontend/pages/build-ibans/add/queries.tsx b/frontend/pages/build-ibans/add/queries.tsx index f965c9c..a4a53fa 100644 --- a/frontend/pages/build-ibans/add/queries.tsx +++ b/frontend/pages/build-ibans/add/queries.tsx @@ -3,7 +3,7 @@ import { useMutation } from '@tanstack/react-query' import { toISOIfNotZ } from '@/lib/utils'; import { BuildIbansAdd } from './schema'; -const fetchGraphQlBuildIbansAdd = async (record: BuildIbansAdd): Promise<{ data: BuildIbansAdd | null; status: number }> => { +const fetchGraphQlBuildIbansAdd = async (record: BuildIbansAdd, buildId: string, refetchTable: () => void): Promise<{ data: BuildIbansAdd | null; status: number }> => { console.log('Fetching test data from local API'); record.expiryStarts = record.expiryStarts ? toISOIfNotZ(record.expiryStarts) : undefined; record.expiryEnds = record.expiryEnds ? toISOIfNotZ(record.expiryEnds) : undefined; @@ -11,16 +11,16 @@ const fetchGraphQlBuildIbansAdd = async (record: BuildIbansAdd): Promise<{ data: record.stopDate = toISOIfNotZ(record.stopDate); console.dir({ record }) try { - const res = await fetch('/api/build-ibans/add', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(record) }); + const res = await fetch(`/api/build-ibans/add?build=${buildId}`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(record) }); 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(); + 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 useAddBuildIbansMutation() { return useMutation({ - mutationFn: ({ data }: { data: BuildIbansAdd }) => fetchGraphQlBuildIbansAdd(data), + mutationFn: ({ data, buildId, refetchTable }: { data: BuildIbansAdd, buildId: string, refetchTable: () => void }) => fetchGraphQlBuildIbansAdd(data, buildId, refetchTable), onSuccess: () => { console.log("Build IBANs created successfully") }, onError: (error) => { console.error("Add build IBANs failed:", error) }, }) diff --git a/frontend/pages/build-ibans/add/table/data-table.tsx b/frontend/pages/build-ibans/add/table/data-table.tsx index 7933420..1118640 100644 --- a/frontend/pages/build-ibans/add/table/data-table.tsx +++ b/frontend/pages/build-ibans/add/table/data-table.tsx @@ -55,6 +55,7 @@ export function BuildIbansDataTableAdd({ onPageChange, onPageSizeChange, refetchTable, + buildId }: { data: schemaType[], totalCount: number, @@ -62,7 +63,8 @@ export function BuildIbansDataTableAdd({ pageSize: number, onPageChange: (page: number) => void, onPageSizeChange: (size: number) => void, - refetchTable: () => void + refetchTable: () => void, + buildId: string }) { const router = useRouter(); @@ -142,9 +144,8 @@ export function BuildIbansDataTableAdd({ })} -
diff --git a/frontend/pages/build-ibans/list/data-table.tsx b/frontend/pages/build-ibans/list/data-table.tsx index d9e3cfb..bc7153d 100644 --- a/frontend/pages/build-ibans/list/data-table.tsx +++ b/frontend/pages/build-ibans/list/data-table.tsx @@ -78,7 +78,8 @@ export function BuildIbansDataTable({ pageSize = 10, onPageChange, onPageSizeChange, - refetchTable + refetchTable, + buildId }: { data: schemaType[], totalCount: number, @@ -87,6 +88,7 @@ export function BuildIbansDataTable({ onPageChange: (page: number) => void, onPageSizeChange: (size: number) => void, refetchTable: () => void, + buildId: string }) { const router = useRouter(); @@ -163,7 +165,7 @@ export function BuildIbansDataTable({ })} - diff --git a/frontend/pages/build-ibans/page.tsx b/frontend/pages/build-ibans/page.tsx index 62fa0bc..bcff919 100644 --- a/frontend/pages/build-ibans/page.tsx +++ b/frontend/pages/build-ibans/page.tsx @@ -2,6 +2,8 @@ import { BuildIbansDataTable } from './list/data-table'; import { useState } from 'react'; import { useGraphQlBuildIbansList } from './queries'; +import { useSearchParams, useRouter } from 'next/navigation'; +import { Button } from '@/components/ui/button'; const PageBuildIbans = () => { @@ -10,7 +12,11 @@ const PageBuildIbans = () => { const [sort, setSort] = useState({ createdAt: 'desc' }); const [filters, setFilters] = useState({}); - const { data, isLoading, error, refetch } = useGraphQlBuildIbansList({ limit, skip: (page - 1) * limit, sort, filters }); + const searchParams = useSearchParams(); + const router = useRouter(); + const buildId = searchParams?.get('build'); + + const { data, isLoading, error, refetch } = useGraphQlBuildIbansList({ limit, skip: (page - 1) * limit, sort, filters: { ...filters, buildId } }); const handlePageChange = (newPage: number) => { setPage(newPage) }; const handlePageSizeChange = (newSize: number) => { setLimit(newSize); setPage(1) }; @@ -18,7 +24,12 @@ const PageBuildIbans = () => { if (isLoading) { return
Loading...
} if (error) { return
Error loading build areas
} - return ; + const noUUIDFound = <> +
Back To Builds. No uuid is found on headers
+ + + if (!buildId) { return noUUIDFound } + return ; }; diff --git a/frontend/pages/build-ibans/update/form.tsx b/frontend/pages/build-ibans/update/form.tsx index d10b69b..3c386d9 100644 --- a/frontend/pages/build-ibans/update/form.tsx +++ b/frontend/pages/build-ibans/update/form.tsx @@ -12,12 +12,9 @@ import { BuildIbansUpdate, buildIbansUpdateSchema } from "@/pages/build-ibans/up const BuildIbansForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () => void, initData: BuildIbansUpdate, selectedUuid: string }) => { const form = useForm({ resolver: zodResolver(buildIbansUpdateSchema), defaultValues: { ...initData } }) - const { handleSubmit } = form - const mutation = useUpdateBuildIbansMutation(); - - function onSubmit(values: BuildIbansUpdate) { mutation.mutate({ data: values as any || initData, uuid: selectedUuid }); setTimeout(() => refetchTable(), 400) } + function onSubmit(values: BuildIbansUpdate) { mutation.mutate({ data: values as any || initData, uuid: selectedUuid, refetchTable }) } return ( diff --git a/frontend/pages/build-ibans/update/page.tsx b/frontend/pages/build-ibans/update/page.tsx index a02811d..736573f 100644 --- a/frontend/pages/build-ibans/update/page.tsx +++ b/frontend/pages/build-ibans/update/page.tsx @@ -24,10 +24,7 @@ const PageUpdateBuildIbans = () => { if (!initData) { return backToBuildAddress } return ( <> - + ) diff --git a/frontend/pages/build-ibans/update/queries.tsx b/frontend/pages/build-ibans/update/queries.tsx index 7feb194..f20de98 100644 --- a/frontend/pages/build-ibans/update/queries.tsx +++ b/frontend/pages/build-ibans/update/queries.tsx @@ -3,23 +3,22 @@ import { useMutation } from '@tanstack/react-query' import { UpdateBuildIbansUpdate } from './types'; import { toISOIfNotZ } from '@/lib/utils'; -const fetchGraphQlBuildIbansUpdate = async (record: UpdateBuildIbansUpdate, uuid: string): Promise<{ data: UpdateBuildIbansUpdate | null; status: number }> => { +const fetchGraphQlBuildIbansUpdate = async (record: UpdateBuildIbansUpdate, uuid: string, refetchTable: () => void): Promise<{ data: UpdateBuildIbansUpdate | null; status: number }> => { console.log('Fetching test data from local API'); record.expiryStarts = record.expiryStarts ? toISOIfNotZ(record.expiryStarts) : undefined; record.expiryEnds = record.expiryEnds ? toISOIfNotZ(record.expiryEnds) : undefined; - record.startDate = toISOIfNotZ(record.startDate); - record.stopDate = toISOIfNotZ(record.stopDate); + record.startDate = toISOIfNotZ(record.startDate); record.stopDate = toISOIfNotZ(record.stopDate); try { const res = await fetch(`/api/build-ibans/update?uuid=${uuid || ''}`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(record) }); 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(); + 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 useUpdateBuildIbansMutation() { return useMutation({ - mutationFn: ({ data, uuid }: { data: UpdateBuildIbansUpdate, uuid: string }) => fetchGraphQlBuildIbansUpdate(data, uuid), + mutationFn: ({ data, uuid, refetchTable }: { data: UpdateBuildIbansUpdate, uuid: string, refetchTable: () => void }) => fetchGraphQlBuildIbansUpdate(data, uuid, refetchTable), onSuccess: () => { console.log("Build IBANs updated successfully") }, onError: (error) => { console.error("Update Build IBANs failed:", error) }, }) diff --git a/frontend/pages/build-ibans/update/table/data-table.tsx b/frontend/pages/build-ibans/update/table/data-table.tsx index 3de63f8..7d30b8a 100644 --- a/frontend/pages/build-ibans/update/table/data-table.tsx +++ b/frontend/pages/build-ibans/update/table/data-table.tsx @@ -87,7 +87,7 @@ export function BuildIbansDataTableUpdate({ pageSize: number, onPageChange: (page: number) => void, onPageSizeChange: (size: number) => void, - refetchTable: () => void + refetchTable: () => void, }) { const router = useRouter(); @@ -100,7 +100,7 @@ export function BuildIbansDataTableUpdate({ const dataIds = React.useMemo(() => data?.map(({ _id }) => _id) || [], [data]) const deleteMutation = useDeletePersonMutation() - const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id }); setTimeout(() => { refetchTable() }, 200) } + const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id, refetchTable }) } const columns = getColumns(router, deleteHandler); const pagination = React.useMemo(() => ({ pageIndex: currentPage - 1, pageSize: pageSize, }), [currentPage, pageSize]) const totalPages = Math.ceil(totalCount / pageSize) @@ -167,7 +167,7 @@ export function BuildIbansDataTableUpdate({ })} - diff --git a/frontend/pages/builds/list/data-table.tsx b/frontend/pages/builds/list/data-table.tsx index 34bf71a..015145e 100644 --- a/frontend/pages/builds/list/data-table.tsx +++ b/frontend/pages/builds/list/data-table.tsx @@ -25,6 +25,7 @@ import { IconChevronRight, IconChevronsLeft, IconChevronsRight, + IconCreditCard, IconLayoutColumns, IconPlus, } from "@tabler/icons-react" @@ -109,9 +110,9 @@ export function BuildDataTable({ icon: }, { - url: 'build-sites', - name: 'Build Sites', - icon: + url: 'build-ibans', + name: 'Build IBANs', + icon: }, ] diff --git a/frontend/pages/living-space/add/page.tsx b/frontend/pages/living-space/add/page.tsx index ff58fa1..ba40025 100644 --- a/frontend/pages/living-space/add/page.tsx +++ b/frontend/pages/living-space/add/page.tsx @@ -22,6 +22,7 @@ const PageLivingSpaceAdd = () => { const [partID, setPartID] = useState(null); const [companyID, setCompanyID] = useState(null); const [personID, setPersonID] = useState(null); + const [isProperty, setIsProperty] = useState(null); const form = createForm({ buildID, userTypeID, partID, companyID, personID }); @@ -60,7 +61,7 @@ const PageLivingSpaceAdd = () => { {isUserTypeEnabled && - + } {isPartsEnabled && buildID && diff --git a/frontend/pages/living-space/list/page.tsx b/frontend/pages/living-space/list/page.tsx index 58053b2..b7d0062 100644 --- a/frontend/pages/living-space/list/page.tsx +++ b/frontend/pages/living-space/list/page.tsx @@ -11,7 +11,6 @@ const PageLivingSpaceList = () => { const router = useRouter(); const [buildID, setBuildID] = useState(null); - // const [collectionToken, setCollectionToken] = useState(null); const [isUserTypeEnabled, setIsUserTypeEnabled] = useState(false); const [page, setPage] = useState(1); const [limit, setLimit] = useState(10); @@ -32,9 +31,7 @@ const PageLivingSpaceList = () => {

Living Space Added to selected Build with collectionToken:

{buildID}

- { - buildID && - } + {buildID && }
; } diff --git a/frontend/pages/living-space/list/queries.tsx b/frontend/pages/living-space/list/queries.tsx index 03158f9..e0f6d50 100644 --- a/frontend/pages/living-space/list/queries.tsx +++ b/frontend/pages/living-space/list/queries.tsx @@ -11,6 +11,19 @@ const fetchGraphQlLivingSpaceList = async (buildID: string, params: ListArgument } catch (error) { console.error('Error fetching test data:', error); throw error } }; +const fetchGraphQlLivingSpaceDetail = async (uuid: string, buildID: string): Promise => { + console.log('Fetching test data from local API'); + try { + const res = await fetch(`/api/living-space/detail`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ uuid, buildID }) }); + 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 } + } catch (error) { console.error('Error fetching test data:', error); throw error } +}; + export function useGraphQlLivingSpaceList(buildID: string, params: ListArguments) { return useQuery({ queryKey: ['graphql-living-space-list', buildID, params], queryFn: () => fetchGraphQlLivingSpaceList(buildID, params) }) } + +export function useGraphQlLivingSpaceDetail(uuid: string, buildID: string) { + return useQuery({ queryKey: ['graphql-living-space-detail', uuid, buildID], queryFn: () => fetchGraphQlLivingSpaceDetail(uuid, buildID) }) +} \ No newline at end of file diff --git a/frontend/pages/living-space/tables/living-spaces/list/columns.tsx b/frontend/pages/living-space/tables/living-spaces/list/columns.tsx index 7bec001..5779615 100644 --- a/frontend/pages/living-space/tables/living-spaces/list/columns.tsx +++ b/frontend/pages/living-space/tables/living-spaces/list/columns.tsx @@ -23,31 +23,62 @@ export function DraggableRow({ row, selectedID }: { row: Row void): ColumnDef[] { +function getColumns(router: any, deleteHandler: (id: string) => void, buildID: string): ColumnDef[] { return [ { accessorKey: "uuid", header: "UUID", }, { - accessorKey: "build", - header: "Build", + accessorKey: "person.firstName", + header: "First Name", }, { - accessorKey: "part", - header: "Part", + accessorKey: "person.middleName", + header: "Middle Name", }, { - accessorKey: "userType", + accessorKey: "person.surname", + header: "Surname", + }, + { + accessorKey: "person.birthDate", + header: "Birth Date", + cell: ({ getValue }) => dateToLocaleString(getValue() as string), + }, + { + accessorKey: "build.info.buildName", + header: "Build Name", + }, + { + accessorKey: "build.info.buildNo", + header: "Build No", + }, + { + accessorKey: "part.no", + header: "Part No", + }, + { + accessorKey: "part.level", + header: "Level", + }, + { + accessorKey: "part.humanLivability", + header: "Livability", + cell: ({ getValue }) => getValue() ?
Yes
:
No
+ }, + { + accessorKey: "person.birthPlace", + header: "Birth Place", + }, + { + accessorKey: "userType.description", header: "User Type", }, { - accessorKey: "company", - header: "Company", - }, - { - accessorKey: "person", - header: "Person", + accessorKey: "userType.isProperty", + header: "Property", + cell: ({ getValue }) => getValue() ?
Yes
:
No
}, { accessorKey: "createdAt", @@ -75,10 +106,10 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef cell: ({ row }) => { return (
- -
diff --git a/frontend/pages/living-space/tables/living-spaces/list/data-table.tsx b/frontend/pages/living-space/tables/living-spaces/list/data-table.tsx index 9fd9bb9..45f7501 100644 --- a/frontend/pages/living-space/tables/living-spaces/list/data-table.tsx +++ b/frontend/pages/living-space/tables/living-spaces/list/data-table.tsx @@ -79,6 +79,7 @@ export function LivingSpaceDataTable({ onPageChange, onPageSizeChange, refetchTable, + buildID }: { data: schemaType[], totalCount: number, @@ -87,6 +88,7 @@ export function LivingSpaceDataTable({ onPageChange: (page: number) => void, onPageSizeChange: (size: number) => void, refetchTable: () => void, + buildID: string }) { const router = useRouter() @@ -99,7 +101,7 @@ export function LivingSpaceDataTable({ const dataIds = React.useMemo(() => data?.map(({ _id }) => _id) || [], [data]) const deleteMutation = useDeleteLivingSpacesMutation() const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id }); setTimeout(() => { refetchTable() }, 400) } - const columns = getColumns(router, deleteHandler); + const columns = getColumns(router, deleteHandler, buildID); const pagination = React.useMemo(() => ({ pageIndex: currentPage - 1, pageSize: pageSize }), [currentPage, pageSize]) const totalPages = Math.ceil(totalCount / pageSize) diff --git a/frontend/pages/living-space/tables/living-spaces/list/queries.tsx b/frontend/pages/living-space/tables/living-spaces/list/queries.tsx index 5451252..83319b0 100644 --- a/frontend/pages/living-space/tables/living-spaces/list/queries.tsx +++ b/frontend/pages/living-space/tables/living-spaces/list/queries.tsx @@ -18,4 +18,3 @@ export function useDeleteLivingSpacesMutation() { onError: (error) => { console.error("Delete living space failed:", error) }, }) } - diff --git a/frontend/pages/living-space/update/page.tsx b/frontend/pages/living-space/update/page.tsx index 8385671..f0bdd0f 100644 --- a/frontend/pages/living-space/update/page.tsx +++ b/frontend/pages/living-space/update/page.tsx @@ -7,10 +7,10 @@ import { useUpdateLivingSpaceMutation } from "./queries"; import { Button } from "@/components/ui/button"; import { IconArrowLeftToArc } from "@tabler/icons-react"; import { useRouter } from "next/navigation"; -import { useGraphQlLivingSpaceList } from "../list/queries"; +import { useGraphQlLivingSpaceDetail } from "../list/queries"; import { useSearchParams } from "next/navigation"; -import PageLivingSpaceBuildsTableSection from "../tables/builds/page"; +// import PageLivingSpaceBuildsTableSection from "../tables/builds/page"; import PageLivingSpaceUserTypesTableSection from "../tables/userType/page"; import PageLivingSpacePartsTableSection from "../tables/part/page"; import PageLivingSpacePersonTableSection from "../tables/person/page"; @@ -22,14 +22,10 @@ const PageLivingSpaceUpdate = () => { const searchParams = useSearchParams() const uuid = searchParams?.get('uuid') || null const buildIDFromUrl = searchParams?.get('buildID') || null - const [page, setPage] = useState(1); - const [limit, setLimit] = useState(10); - const [sort, setSort] = useState({ createdAt: 'desc' }); - const [filters, setFilters] = useState({}); const backToBuildAddress = <>
UUID not found in search params
- const { data, isLoading, refetch } = useGraphQlLivingSpaceList(buildIDFromUrl || '', { limit, skip: (page - 1) * limit, sort, filters: { ...filters, uuid } }); - const initData = data?.data?.[0] || null; + const { data, isLoading, refetch } = useGraphQlLivingSpaceDetail(uuid || '', buildIDFromUrl || ''); + const initData = data?.data || null; const [userTypeID, setUserTypeID] = useState(null); const isPartInit = initData?.part !== null ? true : false; const [isProperty, setIsProperty] = useState(isPartInit); @@ -45,6 +41,7 @@ const PageLivingSpaceUpdate = () => { setUserTypeID(initData?.userType || ""); setPartID(initData?.part || ""); setCompanyID(initData?.company || ""); setPersonID(initData?.person || ""); } }, [initData]) + useEffect(() => { form.setValue("userTypeID", userTypeID || ""); form.setValue("partID", partID || ""); form.setValue("companyID", companyID || ""); form.setValue("personID", personID || ""); }, [userTypeID, partID, companyID, personID, form]); diff --git a/frontend/pages/users/add/form.tsx b/frontend/pages/users/add/form.tsx index 2005192..d268069 100644 --- a/frontend/pages/users/add/form.tsx +++ b/frontend/pages/users/add/form.tsx @@ -20,8 +20,8 @@ const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { expiryEnds: "", isConfirmed: false, isNotificationSend: false, - password: "", - rePassword: "", + // password: "", + // rePassword: "", tag: "", email: "", phone: "" @@ -31,6 +31,7 @@ const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { const [defaultSelection, setDefaultSelection] = useState("") const [selectedBuildIDS, setSelectedBuildIDS] = useState([]) const [selectedCompanyIDS, setSelectedCompanyIDS] = useState([]) + const [personID, setPersonID] = 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)) @@ -40,12 +41,12 @@ const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { const { handleSubmit } = form const mutation = useAddUserMutation(); - function onSubmit(values: UserAdd) { mutation.mutate({ data: values as any, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }); } + function onSubmit(values: UserAdd) { console.dir({ values, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID }); mutation.mutate({ data: values, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID, refetchTable }); } return (
@@ -82,7 +83,7 @@ const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { {/* PASSWORD / TAG */}
- ( @@ -107,7 +108,7 @@ const UserForm = ({ refetchTable }: { refetchTable: () => void }) => { )} - /> + /> */} { const [limit, setLimit] = useState(10); const [sort, setSort] = useState({ createdAt: 'desc' }); const [filters, setFilters] = useState({}); - const { data, isLoading, error, refetch } = useGraphQlUsersList({ limit, skip: (page - 1) * limit, sort, filters }); return ( <> - + ) diff --git a/frontend/pages/users/add/queries.tsx b/frontend/pages/users/add/queries.tsx index 4bdbf27..397da25 100644 --- a/frontend/pages/users/add/queries.tsx +++ b/frontend/pages/users/add/queries.tsx @@ -1,6 +1,6 @@ 'use client' import { useMutation } from '@tanstack/react-query' -import { UserAdd } from './types' +import { UserAdd } from './schema' import { toISOIfNotZ } from '@/lib/utils' const fetchGraphQlUsersAdd = async ( @@ -8,15 +8,16 @@ const fetchGraphQlUsersAdd = async ( selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, + personID: string, refetchTable: () => void ): Promise<{ data: UserAdd | null; status: number }> => { - record.expiryStarts = toISOIfNotZ(record.expiryStarts); - record.expiryEnds = toISOIfNotZ(record.expiryEnds); + record.expiryStarts = record?.expiryStarts ? toISOIfNotZ(record.expiryStarts) : undefined; + record.expiryEnds = record?.expiryEnds ? toISOIfNotZ(record.expiryEnds) : undefined; + const payload = { ...record, person: personID, collectionTokens: { defaultSelection, selectedBuildIDS, selectedCompanyIDS } } try { - const res = await fetch('/api/users/add', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ ...record, selectedBuildIDS, selectedCompanyIDS, defaultSelection }) }); + const res = await fetch('/api/users/add', { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(payload) }); 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(); + const data = await res.json(); refetchTable(); return { data: data.data, status: res.status } } catch (error) { console.error('Error fetching test data:', error); throw error } }; @@ -24,8 +25,12 @@ const fetchGraphQlUsersAdd = async ( export function useAddUserMutation() { return useMutation({ mutationFn: ( - { data, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }: { data: UserAdd, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, refetchTable: () => void } - ) => fetchGraphQlUsersAdd(data, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable), + { + data, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID, refetchTable + }: { + data: UserAdd, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, personID: string, refetchTable: () => void + } + ) => fetchGraphQlUsersAdd(data, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID, 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 5348cda..77659b1 100644 --- a/frontend/pages/users/add/schema.ts +++ b/frontend/pages/users/add/schema.ts @@ -1,15 +1,15 @@ -import { z } from "zod" - +import { z } from "zod"; export const userAddSchema = z.object({ + expiryStarts: z.string().optional(), expiryEnds: z.string().optional(), isConfirmed: z.boolean(), isNotificationSend: z.boolean(), - password: z.string().min(6), - rePassword: z.string().min(6), + // password: z.string().min(6), + // rePassword: z.string().min(6), tag: z.string().optional(), email: z.string().email(), phone: z.string().min(5), diff --git a/frontend/pages/users/add/table/columns.tsx b/frontend/pages/users/add/table/columns.tsx index 7e12ebf..e4f0aca 100644 --- a/frontend/pages/users/add/table/columns.tsx +++ b/frontend/pages/users/add/table/columns.tsx @@ -16,117 +16,6 @@ import { schema, schemaType } from "./schema" import { dateToLocaleString } from "@/lib/utils" import { Pencil, Trash } from "lucide-react" -function TableCellViewer({ item }: { item: schemaType }) { - const isMobile = useIsMobile(); - - return ( - - - - - - - -

{item.email}

-

- User details -

-
- -
- - {/* BASIC INFO */} -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
- - - - {/* DATES */} -
-
- - -
- -
- - -
-
- - - - {/* TOKENS */} - - - - - - - Tokens - - {(item.collectionTokens?.tokens ?? []).length === 0 && (No tokens found)} - {(item.collectionTokens?.tokens ?? []).map((t, i) => ( - -
- - -
-
- ))} -
-
- -
- - - - - - -
-
- ); -} - -function DragHandle({ id }: { id: number }) { - const { attributes, listeners } = useSortable({ id }) - return ( - - ) -} export function DraggableRow({ row }: { row: Row> }) { const { transform, transition, setNodeRef, isDragging } = useSortable({ id: row.original._id }) @@ -161,6 +50,16 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef accessorKey: "tag", header: "Tag", }, + { + accessorKey: "isNotificationSend", + header: "Notificated?", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "isEmailSend", + header: "Email Send?", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, { accessorKey: "active", header: "Active", @@ -171,6 +70,11 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef header: "Confirmed", cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), }, + { + accessorKey: "deleted", + header: "Deleted", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, { accessorKey: "createdAt", header: "Created", @@ -191,57 +95,16 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef header: "Expiry Ends", cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", }, - { - accessorKey: "crypUuId", - header: "Encrypted UUID", - }, - { - accessorKey: "history", - header: "History", - cell: ({ getValue }) => (
{String(getValue() ?? "")}
), - }, { accessorKey: "collectionTokens.tokens", - header: "Tokens", + header: "Default Token", cell: ({ row }) => { - const tokens = row.original.collectionTokens?.tokens ?? []; - const defaultToken = row.original.collectionTokens?.default; - if (!tokens.length) return "-"; + const defaultToken = row.original.collectionTokens?.defaultSelection; return ( - - - - - - Collection Tokens - - {defaultToken && ( - <> - -
- Default - - {defaultToken} - -
-
- - - )} - {tokens.map((t, i) => ( - -
- {t.prefix} - - {t.token} - -
-
- ))} -
-
+ defaultToken ?
+ Default + {defaultToken} +
:
No Default Token is registered.
); }, }, @@ -251,7 +114,7 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef cell: ({ row }) => { return (
-
diff --git a/frontend/pages/users/selections/builds/columns.tsx b/frontend/pages/users/selections/builds/columns.tsx index 2671fe8..ad7cafa 100644 --- a/frontend/pages/users/selections/builds/columns.tsx +++ b/frontend/pages/users/selections/builds/columns.tsx @@ -8,7 +8,7 @@ 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" +import { IconCircleMinus, IconHandClick, IconHandGrab } from "@tabler/icons-react" export function DraggableRow({ row, selectedIDs }: { row: Row>; selectedIDs: string[] }) { const { transform, transition, setNodeRef, isDragging } = useSortable({ id: row.original._id }) @@ -120,18 +120,25 @@ function getColumns(appendBuildID: (id: string) => void, removeBuildID: (id: str header: "Actions", cell: ({ row }) => { return ( - selectedBuildIDS.includes(row.original._id) ? ( -
- -
- ) : -
- -
+
+ {defaultSelection !== row.original._id &&
+
} + {!selectedBuildIDS.includes(row.original._id) ? ( +
+ +
+ ) : +
+ +
+ } +
); }, } diff --git a/frontend/pages/users/selections/builds/page.tsx b/frontend/pages/users/selections/builds/page.tsx index e0f823d..6781e76 100644 --- a/frontend/pages/users/selections/builds/page.tsx +++ b/frontend/pages/users/selections/builds/page.tsx @@ -29,10 +29,8 @@ const PageUsersBuildsTableSection = ( 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 <> + + Drag to reorder + + ) +} + +export function DraggableRow({ row, selectedID }: { row: Row>; selectedID: 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(personID: string, setPersonID: (id: string) => void): ColumnDef[] { + return [ + { + accessorKey: "uuid", + header: "UUID", + cell: ({ getValue }) => (
{String(getValue())}
), + }, + { + accessorKey: "firstName", + header: "First Name", + }, + { + accessorKey: "surname", + header: "Surname", + }, + { + accessorKey: "middleName", + header: "Middle Name", + }, + { + accessorKey: "sexCode", + header: "Sex", + }, + { + accessorKey: "personRef", + header: "Person Ref", + }, + { + accessorKey: "personTag", + header: "Person Tag", + }, + { + accessorKey: "fatherName", + header: "Father Name", + }, + { + accessorKey: "motherName", + header: "Mother Name", + }, + { + accessorKey: "countryCode", + header: "Country", + }, + { + accessorKey: "nationalIdentityId", + header: "National ID", + }, + { + accessorKey: "birthPlace", + header: "Birth Place", + }, + { + accessorKey: "active", + header: "Active", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "isConfirmed", + header: "Confirmed", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "birthDate", + header: "Birth Date", + cell: ({ getValue }) => dateToLocaleString(getValue() as string), + }, + { + 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 ( + personID !== row.original._id && ( +
+ +
+ ) + ); + }, + } + ] +} + +export { getColumns }; \ No newline at end of file diff --git a/frontend/pages/users/selections/people/data-table.tsx b/frontend/pages/users/selections/people/data-table.tsx new file mode 100644 index 0000000..a67e2c3 --- /dev/null +++ b/frontend/pages/users/selections/people/data-table.tsx @@ -0,0 +1,282 @@ +"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" +import { useDeleteUserMutation } from "@/pages/users/queries" +import { TableSkeleton } from "@/components/skeletons/tableSkeleton" + +export default function TableUsersPersonTableSection({ + data, + totalCount, + currentPage = 1, + pageSize = 10, + onPageChange, + onPageSizeChange, + refetchTable, + tableIsLoading, + personID, + setPersonID +}: { + data: schemaType[], + totalCount: number, + currentPage?: number, + pageSize?: number, + onPageChange: (page: number) => void, + onPageSizeChange: (size: number) => void, + refetchTable: () => void, + tableIsLoading: boolean, + personID: string, + setPersonID: (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(personID, setPersonID); + 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())} + + ) + })} + + ))} + + + {tableIsLoading ? + : + {table.getRowModel().rows.map(row => )}} + +
+
+
+
+
+ {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/people/page.tsx b/frontend/pages/users/selections/people/page.tsx new file mode 100644 index 0000000..d3a19eb --- /dev/null +++ b/frontend/pages/users/selections/people/page.tsx @@ -0,0 +1,30 @@ +'use client'; +import { useGraphQlPeopleList } from './queries'; +import { useState } from 'react'; +import TableUsersPersonTableSection from './data-table'; + +const PageUsersPersonTableSection = ({ + personID, + setPersonID +}: { + personID: string; + setPersonID: (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, isFetching, isLoading, error, refetch } = useGraphQlPeopleList({ limit, skip: (page - 1) * limit, sort, filters }); + + const handlePageChange = (newPage: number) => { setPage(newPage) }; + const handlePageSizeChange = (newSize: number) => { setLimit(newSize); setPage(1) }; + if (error) { return
Error loading users
} + + return ; +}; + +export default PageUsersPersonTableSection; diff --git a/frontend/pages/users/selections/people/queries.tsx b/frontend/pages/users/selections/people/queries.tsx new file mode 100644 index 0000000..0b56388 --- /dev/null +++ b/frontend/pages/users/selections/people/queries.tsx @@ -0,0 +1,37 @@ +'use client' +import { useQuery, useMutation } from '@tanstack/react-query' +import { schemaType } from './schema' +import { ListArguments } from '@/types/listRequest' + +const fetchGraphQlPeopleList = 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/people/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 fetchGraphQlDeletePerson = async (uuid: string, refetchTable: () => void): Promise => { + console.log('Fetching test data from local API'); + try { + const res = await fetch(`/api/people/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(); refetchTable(); + return data + } catch (error) { console.error('Error fetching test data:', error); throw error } +}; + +export function useGraphQlPeopleList(params: ListArguments) { + return useQuery({ queryKey: ['graphql-people-list', params], queryFn: () => fetchGraphQlPeopleList(params) }) +} + +export function useDeletePersonMutation() { + return useMutation({ + mutationFn: ({ uuid, refetchTable }: { uuid: string, refetchTable: () => void }) => fetchGraphQlDeletePerson(uuid, refetchTable), + onSuccess: () => { console.log("Person deleted successfully") }, + onError: (error) => { console.error("Delete person failed:", error) }, + }) +} diff --git a/frontend/pages/users/selections/people/schema.tsx b/frontend/pages/users/selections/people/schema.tsx new file mode 100644 index 0000000..02d3e98 --- /dev/null +++ b/frontend/pages/users/selections/people/schema.tsx @@ -0,0 +1,27 @@ +import { z } from "zod"; + +export const schema = z.object({ + _id: z.string(), + uuid: z.string(), + firstName: z.string().nullable().optional(), + surname: z.string().nullable().optional(), + middleName: z.string().nullable().optional(), + sexCode: z.string().nullable().optional(), + personRef: z.string().nullable().optional(), + personTag: z.string().nullable().optional(), + fatherName: z.string().nullable().optional(), + motherName: z.string().nullable().optional(), + countryCode: z.string().nullable().optional(), + nationalIdentityId: z.string().nullable().optional(), + birthPlace: z.string().nullable().optional(), + birthDate: z.string().nullable().optional(), + taxNo: z.string().nullable().optional(), + birthname: z.string().nullable().optional(), + expiryStarts: z.string().nullable().optional(), + expiryEnds: z.string().nullable().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/table/columns.tsx b/frontend/pages/users/table/columns.tsx index 8d64ee2..ee64810 100644 --- a/frontend/pages/users/table/columns.tsx +++ b/frontend/pages/users/table/columns.tsx @@ -16,118 +16,6 @@ import { schema, schemaType } from "./schema" import { dateToLocaleString } from "@/lib/utils" import { Pencil, Trash } from "lucide-react" -function TableCellViewer({ item }: { item: schemaType }) { - const isMobile = useIsMobile(); - - return ( - - - - - - - -

{item.email}

-

- User details -

-
- -
- - {/* BASIC INFO */} -
-
- - -
- -
- - -
- -
- - -
- -
- - -
-
- - - - {/* DATES */} -
-
- - -
- -
- - -
-
- - - - {/* TOKENS */} - - - - - - - Tokens - - {(item.collectionTokens?.tokens ?? []).length === 0 && (No tokens found)} - {(item.collectionTokens?.tokens ?? []).map((t, i) => ( - -
- - -
-
- ))} -
-
- -
- - - - - - -
-
- ); -} - -function DragHandle({ id }: { id: number }) { - const { attributes, listeners } = useSortable({ id }) - return ( - - ) -} - export function DraggableRow({ row }: { row: Row> }) { const { transform, transition, setNodeRef, isDragging } = useSortable({ id: row.original._id }) return ( @@ -161,6 +49,16 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef accessorKey: "tag", header: "Tag", }, + { + accessorKey: "isNotificationSend", + header: "Notificated?", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, + { + accessorKey: "isEmailSend", + header: "Email Send?", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, { accessorKey: "active", header: "Active", @@ -171,6 +69,11 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef header: "Confirmed", cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), }, + { + accessorKey: "deleted", + header: "Deleted", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), + }, { accessorKey: "createdAt", header: "Created", @@ -191,57 +94,16 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef header: "Expiry Ends", cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", }, - { - accessorKey: "crypUuId", - header: "Encrypted UUID", - }, - { - accessorKey: "history", - header: "History", - cell: ({ getValue }) => (
{String(getValue() ?? "")}
), - }, { accessorKey: "collectionTokens.tokens", - header: "Tokens", + header: "Default Token", cell: ({ row }) => { - const tokens = row.original.collectionTokens?.tokens ?? []; - const defaultToken = row.original.collectionTokens?.default; - if (!tokens.length) return "-"; + const defaultToken = row.original.collectionTokens?.defaultSelection; return ( - - - - - - Collection Tokens - - {defaultToken && ( - <> - -
- Default - - {defaultToken} - -
-
- - - )} - {tokens.map((t, i) => ( - -
- {t.prefix} - - {t.token} - -
-
- ))} -
-
+ defaultToken ?
+ Default + {defaultToken} +
:
No Default Token is registered.
); }, }, diff --git a/frontend/pages/users/table/data-table.tsx b/frontend/pages/users/table/data-table.tsx index 946acaf..f32bd9f 100644 --- a/frontend/pages/users/table/data-table.tsx +++ b/frontend/pages/users/table/data-table.tsx @@ -100,7 +100,7 @@ export function UserDataTable({ const dataIds = React.useMemo(() => data?.map(({ _id }) => _id) || [], [data]) const deleteMutation = useDeleteUserMutation() - const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id }); setTimeout(() => { refetchTable() }, 400) } + const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id, refetchTable }) } const columns = getColumns(router, deleteHandler); const pagination = React.useMemo(() => ({ pageIndex: currentPage - 1, pageSize: pageSize }), [currentPage, pageSize]) const totalPages = Math.ceil(totalCount / pageSize) diff --git a/frontend/pages/users/table/schema.tsx b/frontend/pages/users/table/schema.tsx index 52413d2..c44117b 100644 --- a/frontend/pages/users/table/schema.tsx +++ b/frontend/pages/users/table/schema.tsx @@ -2,46 +2,23 @@ import { z } from "zod"; export const schema = z.object({ _id: z.string(), - uuid: z.string().nullable().optional(), - expiryStarts: z.string().nullable().optional(), - expiryEnds: z.string().nullable().optional(), - isConfirmed: z.boolean().nullable().optional(), - deleted: z.boolean().nullable().optional(), - active: z.boolean().nullable().optional(), - crypUuId: z.string().nullable().optional(), - createdCredentialsToken: z.string().nullable().optional(), - updatedCredentialsToken: z.string().nullable().optional(), - confirmedCredentialsToken: z.string().nullable().optional(), - isNotificationSend: z.boolean().nullable().optional(), - isEmailSend: z.boolean().nullable().optional(), - refInt: z.number().nullable().optional(), - refId: z.string().nullable().optional(), - replicationId: z.number().nullable().optional(), - expiresAt: z.string().nullable().optional(), - resetToken: z.string().nullable().optional(), - password: z.string().nullable().optional(), - history: z.array(z.string()).optional(), - tag: z.string().nullable().optional(), - email: z.string().nullable().optional(), - phone: z.string().nullable().optional(), - - collectionTokens: z - .object({ - default: z.string().nullable().optional(), - tokens: z - .array( - z.object({ - prefix: z.string(), - token: z.string(), - }) - ) - .optional(), - }) - .nullable() - .optional(), - - createdAt: z.string().nullable().optional(), - updatedAt: z.string().nullable().optional(), + uuid: z.string(), + expiryStarts: z.string(), + expiryEnds: z.string(), + isConfirmed: z.boolean(), + deleted: z.boolean(), + active: z.boolean(), + isNotificationSend: z.boolean(), + isEmailSend: z.boolean(), + expiresAt: z.string(), + tag: z.string(), + email: z.string(), + phone: z.string().optional(), + collectionTokens: z.object({ + defaultSelection: z.string().nullable().optional(), selectedBuildIDS: z.array(z.string()).optional(), selectedCompanyIDS: z.array(z.string()).optional() + }).nullable().optional(), + createdAt: z.string(), + updatedAt: z.string(), }); export type schemaType = z.infer; diff --git a/frontend/pages/users/types.ts b/frontend/pages/users/types.ts index b7519d4..3f3978c 100644 --- a/frontend/pages/users/types.ts +++ b/frontend/pages/users/types.ts @@ -1,11 +1,7 @@ -interface UserToken { - prefix: string; - token: string; -} - interface CollectionTokens { - default: string; - tokens: UserToken[]; + defaultSelection: string; + selectedBuildIDS: string[]; + selectedCompanyIDS: string[]; } interface User { @@ -16,19 +12,9 @@ interface User { isConfirmed: boolean; deleted: boolean; active: boolean; - crypUuId: string; - createdCredentialsToken: string; - updatedCredentialsToken: string; - confirmedCredentialsToken: string; isNotificationSend: boolean; isEmailSend: boolean; - refInt: number; - refId: string; - replicationId: number; expiresAt: string; - resetToken?: string | null; - password: string; - history: string[]; tag: string; email: string; phone: string; diff --git a/frontend/pages/users/update/form.tsx b/frontend/pages/users/update/form.tsx index 84d7833..d49f734 100644 --- a/frontend/pages/users/update/form.tsx +++ b/frontend/pages/users/update/form.tsx @@ -12,7 +12,7 @@ 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 }) => { +const UserForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () => void, initData: any, selectedUuid: string }) => { const form = useForm({ resolver: zodResolver(userUpdateSchema), @@ -28,10 +28,10 @@ const UserForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () = }) const { handleSubmit } = form - - const [defaultSelection, setDefaultSelection] = useState("") - const [selectedBuildIDS, setSelectedBuildIDS] = useState([]) - const [selectedCompanyIDS, setSelectedCompanyIDS] = useState([]) + const [defaultSelection, setDefaultSelection] = useState(initData.collectionTokens.defaultSelection) + const [selectedBuildIDS, setSelectedBuildIDS] = useState(initData.collectionTokens.selectedBuildIDS) + const [selectedCompanyIDS, setSelectedCompanyIDS] = useState(initData.collectionTokens.selectedCompanyIDS) + const [personID, setPersonID] = useState(initData.person) 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)) @@ -40,12 +40,12 @@ const UserForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () = 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, selectedBuildIDS, selectedCompanyIDS, defaultSelection, refetchTable }); setTimeout(() => refetchTable(), 400) } + function onSubmit(values: UserUpdate) { mutation.mutate({ data: values as any || initData, uuid: selectedUuid, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID, refetchTable }) } return (
@@ -82,33 +82,6 @@ const UserForm = ({ refetchTable, initData, selectedUuid }: { refetchTable: () = {/* PASSWORD / TAG */}
- ( - - Password - - - - - - )} - /> - ( - - Re-Password - - - - - - )} - /> - - +
diff --git a/frontend/pages/users/update/page.tsx b/frontend/pages/users/update/page.tsx index 6c9323c..5c42a5e 100644 --- a/frontend/pages/users/update/page.tsx +++ b/frontend/pages/users/update/page.tsx @@ -24,20 +24,10 @@ const PageUpdateUser = () => { const { data, isLoading, error, refetch } = useGraphQlUsersList({ limit, skip: (page - 1) * limit, sort, filters: { ...filters, uuid } }); const initData = data?.data?.[0] || null; - - if (!initData) { - return <> -
Selected User is either deleted or not found
- - - } - + if (!initData) { return <>
Selected User is either deleted or not found
} return ( <> - + ) diff --git a/frontend/pages/users/update/queries.tsx b/frontend/pages/users/update/queries.tsx index 11ae905..00a36f7 100644 --- a/frontend/pages/users/update/queries.tsx +++ b/frontend/pages/users/update/queries.tsx @@ -1,13 +1,17 @@ 'use client' import { useMutation } from '@tanstack/react-query' -import { UserUpdate } from './types'; +import { UserUpdate } from './schema'; +import { toISOIfNotZ } from '@/lib/utils'; -const fetchGraphQlUsersUpdate = async (record: UserUpdate, uuid: string, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, refetchTable: () => void): Promise<{ data: UserUpdate | null; status: number }> => { +const fetchGraphQlUsersUpdate = async (record: UserUpdate, uuid: string, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, personID: string, refetchTable: () => void): Promise<{ data: UserUpdate | null; status: number }> => { console.log('Fetching test data from local API'); + record.expiryStarts = record?.expiryStarts ? toISOIfNotZ(record.expiryStarts) : undefined; + record.expiryEnds = record?.expiryEnds ? toISOIfNotZ(record.expiryEnds) : undefined; + const payload = { ...record, person: personID, collectionTokens: { defaultSelection, selectedBuildIDS, selectedCompanyIDS } } try { - const res = await fetch(`/api/users/update?uuid=${uuid || ''}`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify({ ...record, selectedBuildIDS, selectedCompanyIDS, defaultSelection }) }); + const res = await fetch(`/api/users/update?uuid=${uuid || ''}`, { method: 'POST', cache: 'no-store', credentials: "include", body: JSON.stringify(payload) }); 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(); + const data = await res.json(); refetchTable(); return { data: data.data, status: res.status } } catch (error) { console.error('Error fetching test data:', error); throw error } }; @@ -15,8 +19,8 @@ const fetchGraphQlUsersUpdate = async (record: UserUpdate, uuid: string, selecte export function useUpdateUserMutation() { return useMutation({ 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), + { data, uuid, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID, refetchTable }: { data: UserUpdate, uuid: string, selectedBuildIDS: string[], selectedCompanyIDS: string[], defaultSelection: string, personID: string, refetchTable: () => void } + ) => fetchGraphQlUsersUpdate(data, uuid, selectedBuildIDS, selectedCompanyIDS, defaultSelection, personID, 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 df7548a..3d0aaf4 100644 --- a/frontend/pages/users/update/schema.ts +++ b/frontend/pages/users/update/schema.ts @@ -8,8 +8,8 @@ export const userUpdateSchema = z.object({ isConfirmed: z.boolean(), isNotificationSend: z.boolean(), - password: z.string().min(6), - rePassword: z.string().min(6), + // password: z.string().min(6), + // rePassword: z.string().min(6), tag: z.string().optional(), email: z.string().email(), phone: z.string().min(5), diff --git a/frontend/pages/users/update/table/columns.tsx b/frontend/pages/users/update/table/columns.tsx index 1098cff..70577a8 100644 --- a/frontend/pages/users/update/table/columns.tsx +++ b/frontend/pages/users/update/table/columns.tsx @@ -24,7 +24,7 @@ export function DraggableRow({ row }: { row: Row> }) { ) } -function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef[] { +function getColumns(deleteHandler: (id: string) => void): ColumnDef[] { return [ { accessorKey: "uuid", @@ -32,48 +32,26 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef cell: ({ getValue }) => (
{String(getValue())}
), }, { - accessorKey: "firstName", - header: "First Name", + accessorKey: "email", + header: "Email", }, { - accessorKey: "surname", - header: "Surname", + accessorKey: "phone", + header: "Phone", }, { - accessorKey: "middleName", - header: "Middle Name", + accessorKey: "tag", + header: "Tag", }, { - accessorKey: "sexCode", - header: "Sex", + accessorKey: "isNotificationSend", + header: "Notificated?", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), }, { - accessorKey: "personRef", - header: "Person Ref", - }, - { - accessorKey: "personTag", - header: "Person Tag", - }, - { - accessorKey: "fatherName", - header: "Father Name", - }, - { - accessorKey: "motherName", - header: "Mother Name", - }, - { - accessorKey: "countryCode", - header: "Country", - }, - { - accessorKey: "nationalIdentityId", - header: "National ID", - }, - { - accessorKey: "birthPlace", - header: "Birth Place", + accessorKey: "isEmailSend", + header: "Email Send?", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), }, { accessorKey: "active", @@ -86,9 +64,9 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), }, { - accessorKey: "birthDate", - header: "Birth Date", - cell: ({ getValue }) => dateToLocaleString(getValue() as string), + accessorKey: "deleted", + header: "Deleted", + cell: ({ getValue }) => getValue() ? (
Yes
) : (
No
), }, { accessorKey: "createdAt", @@ -110,15 +88,36 @@ function getColumns(router: any, deleteHandler: (id: string) => void): ColumnDef header: "Expiry Ends", cell: ({ getValue }) => getValue() ? dateToLocaleString(getValue() as string) : "-", }, + { + accessorKey: "collectionTokens.tokens", + header: "Default Token", + cell: ({ row }) => { + const defaultToken = row.original.collectionTokens?.defaultSelection; + return defaultToken ?
{defaultToken}
:
No Default Token
; + }, + }, + { + accessorKey: "collectionTokens.selectedBuildIDS", + header: "Selected Build IDS", + cell: ({ row }) => { + const selectedBuildIDS = row.original.collectionTokens?.selectedBuildIDS; + return selectedBuildIDS &&
{selectedBuildIDS.length}
; + }, + }, + { + accessorKey: "collectionTokens.selectedCompanyIDS", + header: "Selected Company IDS", + cell: ({ row }) => { + const selectedCompanyIDS = row.original.collectionTokens?.selectedCompanyIDS; + return selectedCompanyIDS &&
{selectedCompanyIDS.length}
; + }, + }, { id: "actions", header: "Actions", cell: ({ row }) => { return (
- diff --git a/frontend/pages/users/update/table/data-table.tsx b/frontend/pages/users/update/table/data-table.tsx index 4828cf2..cc8f626 100644 --- a/frontend/pages/users/update/table/data-table.tsx +++ b/frontend/pages/users/update/table/data-table.tsx @@ -100,7 +100,7 @@ export function UserDataTableUpdate({ const dataIds = React.useMemo(() => data?.map(({ _id }) => _id) || [], [data]) const deleteMutation = useDeleteUserMutation() - const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id }); setTimeout(() => { refetchTable() }, 400) } + const deleteHandler = (id: string) => { deleteMutation.mutate({ uuid: id, refetchTable }) } const columns = getColumns(router, deleteHandler); const pagination = React.useMemo(() => ({ pageIndex: currentPage - 1, pageSize: pageSize, }), [currentPage, pageSize]) const totalPages = Math.ceil(totalCount / pageSize) diff --git a/frontend/pages/users/update/table/schema.tsx b/frontend/pages/users/update/table/schema.tsx index 52413d2..c44117b 100644 --- a/frontend/pages/users/update/table/schema.tsx +++ b/frontend/pages/users/update/table/schema.tsx @@ -2,46 +2,23 @@ import { z } from "zod"; export const schema = z.object({ _id: z.string(), - uuid: z.string().nullable().optional(), - expiryStarts: z.string().nullable().optional(), - expiryEnds: z.string().nullable().optional(), - isConfirmed: z.boolean().nullable().optional(), - deleted: z.boolean().nullable().optional(), - active: z.boolean().nullable().optional(), - crypUuId: z.string().nullable().optional(), - createdCredentialsToken: z.string().nullable().optional(), - updatedCredentialsToken: z.string().nullable().optional(), - confirmedCredentialsToken: z.string().nullable().optional(), - isNotificationSend: z.boolean().nullable().optional(), - isEmailSend: z.boolean().nullable().optional(), - refInt: z.number().nullable().optional(), - refId: z.string().nullable().optional(), - replicationId: z.number().nullable().optional(), - expiresAt: z.string().nullable().optional(), - resetToken: z.string().nullable().optional(), - password: z.string().nullable().optional(), - history: z.array(z.string()).optional(), - tag: z.string().nullable().optional(), - email: z.string().nullable().optional(), - phone: z.string().nullable().optional(), - - collectionTokens: z - .object({ - default: z.string().nullable().optional(), - tokens: z - .array( - z.object({ - prefix: z.string(), - token: z.string(), - }) - ) - .optional(), - }) - .nullable() - .optional(), - - createdAt: z.string().nullable().optional(), - updatedAt: z.string().nullable().optional(), + uuid: z.string(), + expiryStarts: z.string(), + expiryEnds: z.string(), + isConfirmed: z.boolean(), + deleted: z.boolean(), + active: z.boolean(), + isNotificationSend: z.boolean(), + isEmailSend: z.boolean(), + expiresAt: z.string(), + tag: z.string(), + email: z.string(), + phone: z.string().optional(), + collectionTokens: z.object({ + defaultSelection: z.string().nullable().optional(), selectedBuildIDS: z.array(z.string()).optional(), selectedCompanyIDS: z.array(z.string()).optional() + }).nullable().optional(), + createdAt: z.string(), + updatedAt: z.string(), }); export type schemaType = z.infer;