From 8770a98db8148c16836fbe5d467a89d2d2f7b4c9 Mon Sep 17 00:00:00 2001 From: richardtekula Date: Fri, 12 Dec 2025 08:03:29 +0100 Subject: [PATCH] feat: Add company linking to personal contacts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add companyId column to personal_contacts table - Update personal-contact service to include companyName in list - Add getContactsByCompanyId function for company contacts endpoint - Add GET /companies/:companyId/contacts endpoint - Add companyId to contact validation schema 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../personal-contact.controller.js | 11 +++++++ src/db/schema.js | 3 +- src/routes/company.routes.js | 9 ++++++ src/routes/personal-contact.routes.js | 1 + src/services/personal-contact.service.js | 29 +++++++++++++++++-- 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/controllers/personal-contact.controller.js b/src/controllers/personal-contact.controller.js index 2b70588..2ea6fd4 100644 --- a/src/controllers/personal-contact.controller.js +++ b/src/controllers/personal-contact.controller.js @@ -7,6 +7,7 @@ const normalizePayload = (body) => ({ phone: body.phone?.trim(), email: body.email?.trim(), secondaryEmail: body.secondaryEmail?.trim() || null, + companyId: body.companyId || null, }) export const listPersonalContacts = async (req, res, next) => { @@ -57,3 +58,13 @@ export const deletePersonalContact = async (req, res, next) => { next(error) } } + +export const getContactsByCompany = async (req, res, next) => { + try { + const { companyId } = req.params + const contacts = await personalContactService.getContactsByCompanyId(companyId) + res.status(200).json({ success: true, data: contacts }) + } catch (error) { + next(error) + } +} diff --git a/src/db/schema.js b/src/db/schema.js index 50c71e9..700c2e5 100644 --- a/src/db/schema.js +++ b/src/db/schema.js @@ -79,10 +79,11 @@ export const contacts = pgTable('contacts', { accountEmailUnique: unique('account_email_unique').on(table.emailAccountId, table.email), })); -// Personal contacts - osobné kontakty používateľa (bez väzby na firmu alebo email account) +// Personal contacts - osobné kontakty používateľa (s voliteľnou väzbou na firmu) export const personalContacts = pgTable('personal_contacts', { id: uuid('id').primaryKey().defaultRandom(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), + companyId: uuid('company_id').references(() => companies.id, { onDelete: 'set null' }), // voliteľná väzba na firmu firstName: text('first_name').notNull(), lastName: text('last_name'), phone: text('phone').notNull(), diff --git a/src/routes/company.routes.js b/src/routes/company.routes.js index c3b8607..9de01c4 100644 --- a/src/routes/company.routes.js +++ b/src/routes/company.routes.js @@ -1,5 +1,6 @@ import express from 'express'; import * as companyController from '../controllers/company.controller.js'; +import * as personalContactController from '../controllers/personal-contact.controller.js'; import { authenticate } from '../middlewares/auth/authMiddleware.js'; import { requireAdmin } from '../middlewares/auth/roleMiddleware.js'; import { checkCompanyAccess } from '../middlewares/auth/resourceAccessMiddleware.js'; @@ -188,4 +189,12 @@ router.delete( companyController.removeUserFromCompany ); +// Company Contacts (Personal contacts linked to company) +router.get( + '/:companyId/contacts', + validateParams(z.object({ companyId: z.string().uuid() })), + checkCompanyAccess, + personalContactController.getContactsByCompany +); + export default router; diff --git a/src/routes/personal-contact.routes.js b/src/routes/personal-contact.routes.js index 777b500..3525704 100644 --- a/src/routes/personal-contact.routes.js +++ b/src/routes/personal-contact.routes.js @@ -12,6 +12,7 @@ const createContactSchema = z.object({ phone: z.string().min(3, 'Telefón je povinný'), email: z.string().email('Neplatný email'), secondaryEmail: z.union([z.string().email('Neplatný email'), z.literal('')]).optional(), + companyId: z.union([z.string().uuid(), z.literal(''), z.null()]).optional(), }) const updateContactSchema = createContactSchema.partial() diff --git a/src/services/personal-contact.service.js b/src/services/personal-contact.service.js index 11413c3..56f9c84 100644 --- a/src/services/personal-contact.service.js +++ b/src/services/personal-contact.service.js @@ -1,13 +1,36 @@ import { db } from '../config/database.js' -import { personalContacts } from '../db/schema.js' +import { personalContacts, companies } from '../db/schema.js' import { eq, and, desc, ne } from 'drizzle-orm' import { ConflictError, NotFoundError } from '../utils/errors.js' export const listPersonalContacts = async (userId) => { + const contacts = await db + .select({ + id: personalContacts.id, + userId: personalContacts.userId, + companyId: personalContacts.companyId, + firstName: personalContacts.firstName, + lastName: personalContacts.lastName, + phone: personalContacts.phone, + email: personalContacts.email, + secondaryEmail: personalContacts.secondaryEmail, + createdAt: personalContacts.createdAt, + updatedAt: personalContacts.updatedAt, + companyName: companies.name, + }) + .from(personalContacts) + .leftJoin(companies, eq(personalContacts.companyId, companies.id)) + .where(eq(personalContacts.userId, userId)) + .orderBy(desc(personalContacts.updatedAt)) + + return contacts +} + +export const getContactsByCompanyId = async (companyId) => { return db .select() .from(personalContacts) - .where(eq(personalContacts.userId, userId)) + .where(eq(personalContacts.companyId, companyId)) .orderBy(desc(personalContacts.updatedAt)) } @@ -40,6 +63,7 @@ export const createPersonalContact = async (userId, contact) => { .insert(personalContacts) .values({ userId, + companyId: contact.companyId || null, firstName: contact.firstName, lastName: contact.lastName || null, phone: contact.phone, @@ -79,6 +103,7 @@ export const updatePersonalContact = async (contactId, userId, updates) => { if (updates.phone !== undefined) updateData.phone = updates.phone if (updates.email !== undefined) updateData.email = updates.email if (updates.secondaryEmail !== undefined) updateData.secondaryEmail = updates.secondaryEmail + if (updates.companyId !== undefined) updateData.companyId = updates.companyId || null const [updated] = await db .update(personalContacts)