feat: Add company linking to personal contacts
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ const normalizePayload = (body) => ({
|
|||||||
phone: body.phone?.trim(),
|
phone: body.phone?.trim(),
|
||||||
email: body.email?.trim(),
|
email: body.email?.trim(),
|
||||||
secondaryEmail: body.secondaryEmail?.trim() || null,
|
secondaryEmail: body.secondaryEmail?.trim() || null,
|
||||||
|
companyId: body.companyId || null,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const listPersonalContacts = async (req, res, next) => {
|
export const listPersonalContacts = async (req, res, next) => {
|
||||||
@@ -57,3 +58,13 @@ export const deletePersonalContact = async (req, res, next) => {
|
|||||||
next(error)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -79,10 +79,11 @@ export const contacts = pgTable('contacts', {
|
|||||||
accountEmailUnique: unique('account_email_unique').on(table.emailAccountId, table.email),
|
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', {
|
export const personalContacts = pgTable('personal_contacts', {
|
||||||
id: uuid('id').primaryKey().defaultRandom(),
|
id: uuid('id').primaryKey().defaultRandom(),
|
||||||
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
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(),
|
firstName: text('first_name').notNull(),
|
||||||
lastName: text('last_name'),
|
lastName: text('last_name'),
|
||||||
phone: text('phone').notNull(),
|
phone: text('phone').notNull(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import * as companyController from '../controllers/company.controller.js';
|
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 { authenticate } from '../middlewares/auth/authMiddleware.js';
|
||||||
import { requireAdmin } from '../middlewares/auth/roleMiddleware.js';
|
import { requireAdmin } from '../middlewares/auth/roleMiddleware.js';
|
||||||
import { checkCompanyAccess } from '../middlewares/auth/resourceAccessMiddleware.js';
|
import { checkCompanyAccess } from '../middlewares/auth/resourceAccessMiddleware.js';
|
||||||
@@ -188,4 +189,12 @@ router.delete(
|
|||||||
companyController.removeUserFromCompany
|
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;
|
export default router;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const createContactSchema = z.object({
|
|||||||
phone: z.string().min(3, 'Telefón je povinný'),
|
phone: z.string().min(3, 'Telefón je povinný'),
|
||||||
email: z.string().email('Neplatný email'),
|
email: z.string().email('Neplatný email'),
|
||||||
secondaryEmail: z.union([z.string().email('Neplatný email'), z.literal('')]).optional(),
|
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()
|
const updateContactSchema = createContactSchema.partial()
|
||||||
|
|||||||
@@ -1,13 +1,36 @@
|
|||||||
import { db } from '../config/database.js'
|
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 { eq, and, desc, ne } from 'drizzle-orm'
|
||||||
import { ConflictError, NotFoundError } from '../utils/errors.js'
|
import { ConflictError, NotFoundError } from '../utils/errors.js'
|
||||||
|
|
||||||
export const listPersonalContacts = async (userId) => {
|
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
|
return db
|
||||||
.select()
|
.select()
|
||||||
.from(personalContacts)
|
.from(personalContacts)
|
||||||
.where(eq(personalContacts.userId, userId))
|
.where(eq(personalContacts.companyId, companyId))
|
||||||
.orderBy(desc(personalContacts.updatedAt))
|
.orderBy(desc(personalContacts.updatedAt))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,6 +63,7 @@ export const createPersonalContact = async (userId, contact) => {
|
|||||||
.insert(personalContacts)
|
.insert(personalContacts)
|
||||||
.values({
|
.values({
|
||||||
userId,
|
userId,
|
||||||
|
companyId: contact.companyId || null,
|
||||||
firstName: contact.firstName,
|
firstName: contact.firstName,
|
||||||
lastName: contact.lastName || null,
|
lastName: contact.lastName || null,
|
||||||
phone: contact.phone,
|
phone: contact.phone,
|
||||||
@@ -79,6 +103,7 @@ export const updatePersonalContact = async (contactId, userId, updates) => {
|
|||||||
if (updates.phone !== undefined) updateData.phone = updates.phone
|
if (updates.phone !== undefined) updateData.phone = updates.phone
|
||||||
if (updates.email !== undefined) updateData.email = updates.email
|
if (updates.email !== undefined) updateData.email = updates.email
|
||||||
if (updates.secondaryEmail !== undefined) updateData.secondaryEmail = updates.secondaryEmail
|
if (updates.secondaryEmail !== undefined) updateData.secondaryEmail = updates.secondaryEmail
|
||||||
|
if (updates.companyId !== undefined) updateData.companyId = updates.companyId || null
|
||||||
|
|
||||||
const [updated] = await db
|
const [updated] = await db
|
||||||
.update(personalContacts)
|
.update(personalContacts)
|
||||||
|
|||||||
Reference in New Issue
Block a user