diff --git a/apps/design-system/src/components/Atoms/Form/Checkbox/Checkboxes.tsx b/apps/design-system/src/components/Atoms/Form/Checkbox/Checkboxes.tsx index e3dad6fb..5caf2f0c 100644 --- a/apps/design-system/src/components/Atoms/Form/Checkbox/Checkboxes.tsx +++ b/apps/design-system/src/components/Atoms/Form/Checkbox/Checkboxes.tsx @@ -10,7 +10,7 @@ interface CheckboxItem { interface CheckboxProps { name: string - values: [] + values: string[] label: string | ReactElement> description?: string error?: string diff --git a/apps/envited.ascs.digital/app/dashboard/profile/page.tsx b/apps/envited.ascs.digital/app/dashboard/profile/page.tsx index 4a81cff0..dd31fda4 100644 --- a/apps/envited.ascs.digital/app/dashboard/profile/page.tsx +++ b/apps/envited.ascs.digital/app/dashboard/profile/page.tsx @@ -3,9 +3,9 @@ import { Profile } from '../../../modules/Profile' export default async function Index() { const profile = await getProfile('testcompany-gmbh') - const memberCategories = await getBusinessCategories() + const businessCategories = await getBusinessCategories() - return + return } export const dynamic = 'force-dynamic' diff --git a/apps/envited.ascs.digital/common/database/queries/businessCategories.ts b/apps/envited.ascs.digital/common/database/queries/businessCategories.ts index d3422589..44fe10a9 100644 --- a/apps/envited.ascs.digital/common/database/queries/businessCategories.ts +++ b/apps/envited.ascs.digital/common/database/queries/businessCategories.ts @@ -1,4 +1,22 @@ -import { businessCategory } from '../schema' +import { eq } from 'drizzle-orm' + +import { businessCategory, profilesToBusinessCategories } from '../schema' import { DatabaseConnection } from '../types' export const getBusinessCategories = (db: DatabaseConnection) => async () => db.select().from(businessCategory) + +export const getBusinessCategoriesByProfileId = (db: DatabaseConnection) => async (profileId: string) => + db + .select({ businessCategoryId: profilesToBusinessCategories.businessCategoryId }) + .from(profilesToBusinessCategories) + .where(eq(profilesToBusinessCategories.profileId, profileId)) + +export const insertBusinessCategoryByProfileId = + (db: DatabaseConnection) => async (profileId: string, businessCategoryId: string) => + db.insert(profilesToBusinessCategories).values({ + profileId, + businessCategoryId, + }) + +export const deleteBusinessCategoriesByProfileId = (db: DatabaseConnection) => async (profileId: string) => + db.delete(profilesToBusinessCategories).where(eq(profilesToBusinessCategories.profileId, profileId)) diff --git a/apps/envited.ascs.digital/common/database/queries/profiles.ts b/apps/envited.ascs.digital/common/database/queries/profiles.ts index 3e8dc444..3ee727a4 100644 --- a/apps/envited.ascs.digital/common/database/queries/profiles.ts +++ b/apps/envited.ascs.digital/common/database/queries/profiles.ts @@ -20,7 +20,12 @@ export const maybeUpdatePublishedState = (db: DatabaseConnection) => async (data } export const getProfileBySlug = (db: DatabaseConnection) => async (slug: string) => - db.select().from(profile).where(eq(profile.slug, slug)) + db.query.profile.findFirst({ + where: eq(profile.slug, slug), + with: { + businessCategories: true, + }, + }) export const getPublishedProfiles = (db: DatabaseConnection) => async () => db.select().from(profile).where(eq(profile.isPublished, true)) diff --git a/apps/envited.ascs.digital/common/database/queries/queries.ts b/apps/envited.ascs.digital/common/database/queries/queries.ts index 11917b3b..9b62b858 100644 --- a/apps/envited.ascs.digital/common/database/queries/queries.ts +++ b/apps/envited.ascs.digital/common/database/queries/queries.ts @@ -4,7 +4,12 @@ import { fromPairs, map, pipe, toPairs } from 'ramda' import { connectDb } from '../database' import * as schema from '../schema' -import { getBusinessCategories } from './businessCategories' +import { + deleteBusinessCategoriesByProfileId, + getBusinessCategories, + getBusinessCategoriesByProfileId, + insertBusinessCategoryByProfileId, +} from './businessCategories' import { fetchTables } from './common' import { getProfileBySlug, getPublishedProfiles, maybeUpdatePublishedState, update as updateProfile } from './profiles' import { @@ -18,11 +23,14 @@ import { const queries = { deleteUserById, + deleteBusinessCategoriesByProfileId, fetchTables, getBusinessCategories, + getBusinessCategoriesByProfileId, getUserById, getUserWithProfileById, getUsersByIssuerId, + insertBusinessCategoryByProfileId, insertUserTx, updateProfile, maybeUpdatePublishedState, @@ -38,7 +46,7 @@ export const init = string, ( db: PostgresJsDatabase, - ) => (...args: any[]) => Promise[]> | postgres.Row> + ) => (...args: any[]) => Promise[]> | postgres.Row | undefined> >, ) => async () => { diff --git a/apps/envited.ascs.digital/common/database/schema.ts b/apps/envited.ascs.digital/common/database/schema.ts index b2b08372..c1566864 100644 --- a/apps/envited.ascs.digital/common/database/schema.ts +++ b/apps/envited.ascs.digital/common/database/schema.ts @@ -138,6 +138,10 @@ export const profile = pgTable('profile', { updatedAt: timestamp('updated_at'), }) +export const profileRelations = relations(profile, ({ many }) => ({ + businessCategories: many(profilesToBusinessCategories), +})) + export const businessCategory = pgTable('businessCategory', { id: text('id').unique().primaryKey(), name: text('name').unique(), @@ -146,6 +150,10 @@ export const businessCategory = pgTable('businessCategory', { updatedAt: timestamp('updated_at'), }) +export const businessCategoryRelations = relations(businessCategory, ({ many }) => ({ + profilesToBusinessCategories: many(profilesToBusinessCategories), +})) + export const profilesToBusinessCategories = pgTable('profilesToBusinessCategories', { profileId: uuid('profile_id') .references(() => profile.id) diff --git a/apps/envited.ascs.digital/common/serverActions/businessCategories/get.ts b/apps/envited.ascs.digital/common/serverActions/businessCategories/get.ts index f3c0dba9..99c0c4e5 100644 --- a/apps/envited.ascs.digital/common/serverActions/businessCategories/get.ts +++ b/apps/envited.ascs.digital/common/serverActions/businessCategories/get.ts @@ -30,3 +30,25 @@ export const _get = } export const get = _get({ db, getServerSession, log }) + +export const _getProfileBusinessCategories = + ({ db, getServerSession, log }: { db: Database; getServerSession: () => Promise; log: Log }) => + async (profileId: string) => { + try { + const session = await getServerSession() + + if (isNil(session)) { + throw unauthorizedError({ resource: 'businessCategories' }) + } + + const connection = await db() + const categories = await connection.getBusinessCategoriesByProfileId(profileId) + + return categories + } catch (error: unknown) { + log.error(formatError(error)) + throw internalServerErrorError() + } + } + +export const getProfileBusinessCategories = _getProfileBusinessCategories({ db, getServerSession, log }) diff --git a/apps/envited.ascs.digital/common/serverActions/profiles/get.test.ts b/apps/envited.ascs.digital/common/serverActions/profiles/get.test.ts index acb30312..77c95c25 100644 --- a/apps/envited.ascs.digital/common/serverActions/profiles/get.test.ts +++ b/apps/envited.ascs.digital/common/serverActions/profiles/get.test.ts @@ -15,13 +15,11 @@ describe('serverActions/profiles/get', () => { }) const dbStub = jest.fn().mockResolvedValue({ - getProfileBySlug: jest.fn().mockResolvedValue([ - { - name: 'USER_PRINCIPAL_NAME', - description: 'USER_DESCRIPTION', - principalName: 'USER_PRINCIPAL_NAME', - }, - ]), + getProfileBySlug: jest.fn().mockResolvedValue({ + name: 'USER_PRINCIPAL_NAME', + description: 'USER_DESCRIPTION', + principalName: 'USER_PRINCIPAL_NAME', + }), getUserById: jest.fn().mockResolvedValue([ { name: 'USER_PRINCIPAL_NAME', @@ -63,13 +61,11 @@ describe('serverActions/profiles/get', () => { }) const dbStub = jest.fn().mockResolvedValue({ - getProfileBySlug: jest.fn().mockResolvedValue([ - { - name: 'USER_PRINCIPAL_NAME', - description: 'USER_DESCRIPTION', - principalName: 'USER_PRINCIPAL_NAME', - }, - ]), + getProfileBySlug: jest.fn().mockResolvedValue({ + name: 'USER_PRINCIPAL_NAME', + description: 'USER_DESCRIPTION', + principalName: 'USER_PRINCIPAL_NAME', + }), getUserById: jest.fn().mockResolvedValue([ { name: 'USER_NAME', @@ -106,13 +102,11 @@ describe('serverActions/profiles/get', () => { const getServerSessionStub = jest.fn().mockResolvedValue(null) const dbStub = jest.fn().mockResolvedValue({ - getProfileBySlug: jest.fn().mockResolvedValue([ - { - name: 'USER_NAME', - description: 'USER_DESCRIPTION', - principalName: 'USER_PRINCIPAL_NAME', - }, - ]), + getProfileBySlug: jest.fn().mockResolvedValue({ + name: 'USER_NAME', + description: 'USER_DESCRIPTION', + principalName: 'USER_PRINCIPAL_NAME', + }), getUserById: jest.fn().mockResolvedValue([ { name: 'USER_NAME', diff --git a/apps/envited.ascs.digital/common/serverActions/profiles/get.ts b/apps/envited.ascs.digital/common/serverActions/profiles/get.ts index 84be1f54..7453e3d1 100644 --- a/apps/envited.ascs.digital/common/serverActions/profiles/get.ts +++ b/apps/envited.ascs.digital/common/serverActions/profiles/get.ts @@ -9,7 +9,7 @@ import { Database } from '../../database/types' import { isOwnProfile, isUsersCompanyProfile } from '../../guards' import { Log, log } from '../../logger' import { Session } from '../../types' -import { badRequestError, formatError, internalServerErrorError, notFoundError, unauthorizedError } from '../../utils' +import { badRequestError, formatError, internalServerErrorError, notFoundError } from '../../utils' export const _get = ({ db, getServerSession, log }: { db: Database; getServerSession: () => Promise; log: Log }) => @@ -21,7 +21,7 @@ export const _get = const session = await getServerSession() const connection = await db() - const [profile] = await connection.getProfileBySlug(slug) + const profile = await connection.getProfileBySlug(slug) if (isNil(profile) || isEmpty(profile)) { throw notFoundError({ resource: 'profiles', resourceId: slug, userId: session?.user.id }) diff --git a/apps/envited.ascs.digital/common/serverActions/profiles/update.ts b/apps/envited.ascs.digital/common/serverActions/profiles/update.ts index fbeb42f7..3ffa5024 100644 --- a/apps/envited.ascs.digital/common/serverActions/profiles/update.ts +++ b/apps/envited.ascs.digital/common/serverActions/profiles/update.ts @@ -1,6 +1,6 @@ 'use server' -import { isNil } from 'ramda' +import { dissoc, isNil } from 'ramda' import { getServerSession } from '../../auth' import { db } from '../../database/queries' @@ -12,7 +12,7 @@ import { badRequestError, forbiddenError, formatError, internalServerErrorError, export const _update = ({ db, getServerSession, log }: { db: Database; getServerSession: () => Promise; log: Log }) => - async (profile: Partial) => { + async (profile: Partial, businessCategories?: string[]) => { try { const session = await getServerSession() if (isNil(session)) { @@ -50,6 +50,16 @@ export const _update = } const [updatedProfile] = await connection.updateProfile(profile) + + if (businessCategories) { + await connection.deleteBusinessCategoriesByProfileId(updatedProfile.id) + + const insertProfilesToBusinessCategoriesPromises = businessCategories.map((id: string) => + connection.insertBusinessCategoryByProfileId(updatedProfile.id, id), + ) + + await Promise.all(insertProfilesToBusinessCategoriesPromises) + } const [result] = await connection.maybeUpdatePublishedState(updatedProfile) return result } catch (error: unknown) { diff --git a/apps/envited.ascs.digital/drizzle/development/0010_third_fallen_one.sql b/apps/envited.ascs.digital/drizzle/development/0010_third_fallen_one.sql new file mode 100644 index 00000000..8c406d21 --- /dev/null +++ b/apps/envited.ascs.digital/drizzle/development/0010_third_fallen_one.sql @@ -0,0 +1,39 @@ +ALTER TABLE "profile" DROP CONSTRAINT "profile_name_user_name_fk"; +--> statement-breakpoint +ALTER TABLE "profilesToBusinessCategories" DROP CONSTRAINT "profilesToBusinessCategories_profile_id_profile_id_fk"; +--> statement-breakpoint +ALTER TABLE "user" DROP CONSTRAINT "user_issuer_id_issuer_id_fk"; +--> statement-breakpoint +ALTER TABLE "usersToCredentialTypes" DROP CONSTRAINT "usersToCredentialTypes_user_id_user_id_fk"; +--> statement-breakpoint +ALTER TABLE "usersToRoles" DROP CONSTRAINT "usersToRoles_user_id_user_id_fk"; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "profile" ADD CONSTRAINT "profile_name_user_name_fk" FOREIGN KEY ("name") REFERENCES "user"("name") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "profilesToBusinessCategories" ADD CONSTRAINT "profilesToBusinessCategories_profile_id_profile_id_fk" FOREIGN KEY ("profile_id") REFERENCES "profile"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "user" ADD CONSTRAINT "user_issuer_id_issuer_id_fk" FOREIGN KEY ("issuer_id") REFERENCES "issuer"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "usersToCredentialTypes" ADD CONSTRAINT "usersToCredentialTypes_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "usersToRoles" ADD CONSTRAINT "usersToRoles_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/apps/envited.ascs.digital/drizzle/development/meta/0010_snapshot.json b/apps/envited.ascs.digital/drizzle/development/meta/0010_snapshot.json new file mode 100644 index 00000000..1f5e3537 --- /dev/null +++ b/apps/envited.ascs.digital/drizzle/development/meta/0010_snapshot.json @@ -0,0 +1,743 @@ +{ + "id": "b9761f32-5215-4d1a-9df7-9c4941bb5472", + "prevId": "ce044679-56bc-440c-9681-39abb67edd6f", + "version": "5", + "dialect": "pg", + "tables": { + "addressType": { + "name": "addressType", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "addressType_name_unique": { + "name": "addressType_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + } + }, + "businessCategory": { + "name": "businessCategory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "businessCategory_id_unique": { + "name": "businessCategory_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + }, + "businessCategory_name_unique": { + "name": "businessCategory_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + } + }, + "credentialType": { + "name": "credentialType", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "issuer": { + "name": "issuer", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "issuer_id_unique": { + "name": "issuer_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + } + }, + "profile": { + "name": "profile", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "street_address": { + "name": "street_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postal_code": { + "name": "postal_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_locality": { + "name": "address_locality", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_country": { + "name": "address_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_name": { + "name": "sales_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_phone": { + "name": "sales_phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sales_email": { + "name": "sales_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "principal_name": { + "name": "principal_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "principal_phone": { + "name": "principal_phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "principal_email": { + "name": "principal_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "offerings": { + "name": "offerings", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_published": { + "name": "is_published", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "profile_name_user_name_fk": { + "name": "profile_name_user_name_fk", + "tableFrom": "profile", + "tableTo": "user", + "columnsFrom": [ + "name" + ], + "columnsTo": [ + "name" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "profile_name_unique": { + "name": "profile_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + }, + "profile_slug_unique": { + "name": "profile_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + } + }, + "profilesToBusinessCategories": { + "name": "profilesToBusinessCategories", + "schema": "", + "columns": { + "profile_id": { + "name": "profile_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "business_category_id": { + "name": "business_category_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "profilesToBusinessCategories_profile_id_profile_id_fk": { + "name": "profilesToBusinessCategories_profile_id_profile_id_fk", + "tableFrom": "profilesToBusinessCategories", + "tableTo": "profile", + "columnsFrom": [ + "profile_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "profilesToBusinessCategories_business_category_id_businessCategory_id_fk": { + "name": "profilesToBusinessCategories_business_category_id_businessCategory_id_fk", + "tableFrom": "profilesToBusinessCategories", + "tableTo": "businessCategory", + "columnsFrom": [ + "business_category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "role": { + "name": "role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "role_id_unique": { + "name": "role_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + } + } + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_ascs_member": { + "name": "is_ascs_member", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "is_envited_member": { + "name": "is_envited_member", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "street_address": { + "name": "street_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postal_code": { + "name": "postal_code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_locality": { + "name": "address_locality", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "address_country": { + "name": "address_country", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vat_id": { + "name": "vat_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "privacy_policy_accepted": { + "name": "privacy_policy_accepted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "articles_of_association_accepted": { + "name": "articles_of_association_accepted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "contribution_rules_accepted": { + "name": "contribution_rules_accepted", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "address_type_id": { + "name": "address_type_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuance_date": { + "name": "issuance_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expiration_date": { + "name": "expiration_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "is_active": { + "name": "is_active", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_issuer_id_issuer_id_fk": { + "name": "user_issuer_id_issuer_id_fk", + "tableFrom": "user", + "tableTo": "issuer", + "columnsFrom": [ + "issuer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_address_type_id_addressType_id_fk": { + "name": "user_address_type_id_addressType_id_fk", + "tableFrom": "user", + "tableTo": "addressType", + "columnsFrom": [ + "address_type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_id_unique": { + "name": "user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "id" + ] + }, + "user_name_unique": { + "name": "user_name_unique", + "nullsNotDistinct": false, + "columns": [ + "name" + ] + } + } + }, + "usersToCredentialTypes": { + "name": "usersToCredentialTypes", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "credential_type_id": { + "name": "credential_type_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "usersToCredentialTypes_user_id_user_id_fk": { + "name": "usersToCredentialTypes_user_id_user_id_fk", + "tableFrom": "usersToCredentialTypes", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "usersToCredentialTypes_credential_type_id_credentialType_id_fk": { + "name": "usersToCredentialTypes_credential_type_id_credentialType_id_fk", + "tableFrom": "usersToCredentialTypes", + "tableTo": "credentialType", + "columnsFrom": [ + "credential_type_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "usersToRoles": { + "name": "usersToRoles", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "usersToRoles_user_id_user_id_fk": { + "name": "usersToRoles_user_id_user_id_fk", + "tableFrom": "usersToRoles", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "usersToRoles_role_id_role_id_fk": { + "name": "usersToRoles_role_id_role_id_fk", + "tableFrom": "usersToRoles", + "tableTo": "role", + "columnsFrom": [ + "role_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/envited.ascs.digital/drizzle/development/meta/_journal.json b/apps/envited.ascs.digital/drizzle/development/meta/_journal.json index b0753697..4c39b693 100644 --- a/apps/envited.ascs.digital/drizzle/development/meta/_journal.json +++ b/apps/envited.ascs.digital/drizzle/development/meta/_journal.json @@ -71,6 +71,13 @@ "when": 1707993475646, "tag": "0009_overjoyed_blockbuster", "breakpoints": true + }, + { + "idx": 10, + "version": "5", + "when": 1709215773695, + "tag": "0010_third_fallen_one", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/envited.ascs.digital/modules/Profile/Profile.actions.ts b/apps/envited.ascs.digital/modules/Profile/Profile.actions.ts index b000d021..80afe94f 100644 --- a/apps/envited.ascs.digital/modules/Profile/Profile.actions.ts +++ b/apps/envited.ascs.digital/modules/Profile/Profile.actions.ts @@ -40,7 +40,7 @@ export async function updateProfileForm(data: ProfileForm) { data = dissoc('file')(set(lens(prop('logo'), assoc('logo')), image.url.split('?')[0], data)) } - await updateProfile(data) + await updateProfile(dissoc('businessCategories')(data), data.businessCategories) revalidatePath('/dashboard/profile') } catch (error: unknown) { diff --git a/apps/envited.ascs.digital/modules/Profile/Profile.schema.ts b/apps/envited.ascs.digital/modules/Profile/Profile.schema.ts index 70428366..e5f07d66 100644 --- a/apps/envited.ascs.digital/modules/Profile/Profile.schema.ts +++ b/apps/envited.ascs.digital/modules/Profile/Profile.schema.ts @@ -21,6 +21,7 @@ export const ProfileSchema = z.object({ principalPhone: z.string(), principalEmail: z.string().email().optional().or(z.literal('')), website: z.string().url().optional().or(z.literal('')), + businessCategories: z.string().array().optional(), offerings: z .object({ name: z.string().max(100), diff --git a/apps/envited.ascs.digital/modules/Profile/Profile.test.tsx b/apps/envited.ascs.digital/modules/Profile/Profile.test.tsx index e4e81d22..988c5d15 100644 --- a/apps/envited.ascs.digital/modules/Profile/Profile.test.tsx +++ b/apps/envited.ascs.digital/modules/Profile/Profile.test.tsx @@ -23,7 +23,9 @@ describe('Profile', () => { isPublished: true, } - const { baseElement } = render() + const { baseElement } = render( + , + ) expect(baseElement).toBeTruthy() }) }) diff --git a/apps/envited.ascs.digital/modules/Profile/Profile.tsx b/apps/envited.ascs.digital/modules/Profile/Profile.tsx index 1db8b886..81f371a8 100644 --- a/apps/envited.ascs.digital/modules/Profile/Profile.tsx +++ b/apps/envited.ascs.digital/modules/Profile/Profile.tsx @@ -1,8 +1,16 @@ 'use client' -import { Card, DragAndDropField, Heading, TextField, TextareaField } from '@envited-marketplace/design-system' +import { + Card, + Checkboxes, + DragAndDropField, + Heading, + LoadingIndicator, + TextField, + TextareaField, +} from '@envited-marketplace/design-system' import { zodResolver } from '@hookform/resolvers/zod' -import { pathOr, prop, propOr } from 'ramda' +import { append, chain, equals, includes, isNil, pathOr, prop, propOr, reject } from 'ramda' import { FC } from 'react' import { Controller, SubmitHandler, useFieldArray, useForm } from 'react-hook-form' @@ -13,9 +21,16 @@ import { mapIndexed } from '../../common/utils' import { updateProfileForm } from './Profile.actions' import { ProfileSchema } from './Profile.schema' +interface BusinessCategories { + profileId: string + businessCategoryId: string +} +interface Profiles extends ProfileType { + businessCategories: BusinessCategories[] +} interface ProfileProps { - profile: ProfileType - memberCategories: any[] + profile: Profiles + businessCategories: any[] } interface OfferingItem { @@ -42,10 +57,11 @@ type ProfileInputs = { principalPhone: string principalEmail: string website: string + businessCategories: string[] offerings: OfferingItem[] | [] } -export const Profile: FC = ({ profile, memberCategories }) => { +export const Profile: FC = ({ profile, businessCategories }) => { const { t } = useTranslation('Profile') const { error, success } = useNotification() @@ -71,11 +87,20 @@ export const Profile: FC = ({ profile, memberCategories }) => { principalPhone: propOr('', 'principalPhone')(profile), principalEmail: propOr('', 'principalEmail')(profile), website: propOr('', 'website')(profile), + businessCategories: !isNil(profile?.businessCategories) + ? (chain(prop('businessCategoryId'))(profile?.businessCategories as any) as string[]) + : [], offerings: propOr([], 'offerings')(profile), }, mode: 'onChange', }) + const handleCheckbox = (checkId: string) => { + const { businessCategories: ids } = getValues() + + return includes(checkId)(ids) ? reject(equals(checkId))(ids) : append(checkId)(ids) + } + const { fields: offeringFields, append: appendOffering, @@ -131,37 +156,35 @@ export const Profile: FC = ({ profile, memberCategories }) => {
( - ( + )} /> - {/* +
+ +
( - ( + { - if (event.target.files) { - onChange(event.target.files?.[0]) - } - }} - error={pathOr('', ['file', 'message'])(errors)} + {...field} + error={pathOr('', ['logo', 'message'])(errors)} /> )} /> - */}