Files
helpdesk-texnet/backend/prisma/seed.ts
pettrop da265ff097 Revízny systém - kompletná implementácia
- Backend: CRUD revízií, schedule endpoint (agregovaný plán), skip revízia, stats
- Shared utility revisionSchedule.ts - centralizovaná logika výpočtu cyklov
- Equipment detail s revíznym plánom, históriou a prílohami
- Frontend: RevisionsList s tabmi (nadchádzajúce/po termíne/vykonané/preskočené)
- Pozičné labeling cyklov (eliminuje drift 4×90≠365)
- EquipmentRevisionSchedule model (many-to-many typy revízií)
- Aktualizovaná dokumentácia HELPDESK_INIT_V2.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 21:59:23 +01:00

717 lines
25 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';
import {
AUDIT_MAP,
PERIOD_TO_TYPE,
ADDITIONAL_EQUIP_TYPES,
CUSTOMERS,
EQUIPMENT,
SERVICE_RECORDS,
} from './seed-data';
const prisma = new PrismaClient();
/**
* Klasifikuje poznámku revízie na kód typu revízie.
* Staré dáta používali voľný text na zaznamenanie druhu revízie.
*/
function classifyNote(note: string, basePeriodicity: number): string {
const normalized = note.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
// Štvrťročná (rôzne varianty s preklepmi) - musí byť pred ročnou, lebo "stvrtrocna" obsahuje "rocn"
// s[trv]{1,4}rocn pokrýva: stvrtrocn, svrtrocn, strtrocn, strvtrocn a ďalšie preklepy
if (/s[trv]{1,4}rocn/.test(normalized)) return 'QUARTERLY';
// Ročná (vrátane preklepu "Ričná")
if (/rocn|ricn/.test(normalized)) return 'ANNUAL';
// Mesačná
if (/mesacn/.test(normalized)) return 'MONTHLY';
// Východzia / Vychodisková → základný typ z auditu
if (/vychodz|vychodisk/.test(normalized)) return PERIOD_TO_TYPE[basePeriodicity] || 'QUARTERLY';
// Default → typ z auditu
return PERIOD_TO_TYPE[basePeriodicity] || 'QUARTERLY';
}
/**
* Zistí, či poznámka indikuje "Východziu revíziu".
*/
function isVychodziaNote(note: string): boolean {
const normalized = note.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
return /vychodz|vychodisk/.test(normalized);
}
async function seed() {
console.log('Seeding database...');
// ===== USER ROLES =====
console.log('Creating user roles...');
const roles = await Promise.all([
prisma.userRole.upsert({
where: { code: 'ROOT' },
update: {},
create: {
code: 'ROOT',
name: 'Root Správca',
level: 1,
order: 1,
permissions: {
projects: ['*'],
tasks: ['*'],
equipment: ['*'],
rma: ['*'],
customers: ['*'],
settings: ['*'],
users: ['*'],
logs: ['*'],
},
},
}),
prisma.userRole.upsert({
where: { code: 'ADMIN' },
update: {},
create: {
code: 'ADMIN',
name: 'Administrátor',
level: 2,
order: 2,
permissions: {
projects: ['create', 'read', 'update', 'delete', 'all'],
tasks: ['create', 'read', 'update', 'delete', 'all'],
equipment: ['create', 'read', 'update', 'delete', 'all'],
rma: ['create', 'read', 'update', 'delete', 'approve'],
customers: ['create', 'read', 'update', 'delete'],
users: ['read'],
},
},
}),
prisma.userRole.upsert({
where: { code: 'USER' },
update: {},
create: {
code: 'USER',
name: 'Používateľ',
level: 3,
order: 3,
permissions: {
projects: ['read', 'update'],
tasks: ['create', 'read', 'update'],
equipment: ['read', 'update'],
rma: ['create', 'read', 'update'],
customers: ['read'],
},
},
}),
prisma.userRole.upsert({
where: { code: 'CUSTOMER' },
update: {},
create: {
code: 'CUSTOMER',
name: 'Zákazník',
level: 4,
order: 4,
permissions: {
projects: ['read'],
tasks: ['read'],
equipment: ['read'],
rma: ['create', 'read'],
},
},
}),
]);
// ===== EQUIPMENT TYPES =====
console.log('Creating equipment types...');
await prisma.equipmentType.createMany({
skipDuplicates: true,
data: [
{ code: 'EPS', name: 'Elektrická požiarna signalizácia', color: '#3B82F6', order: 1 },
{ code: 'HSP', name: 'Hlasová signalizácia požiaru', color: '#EF4444', order: 2 },
{ code: 'CAMERA', name: 'Kamerový systém', color: '#10B981', order: 3 },
{ code: 'ACCESS', name: 'Prístupový systém', color: '#F59E0B', order: 4 },
{ code: 'OTHER', name: 'Iné zariadenie', color: '#6B7280', order: 5 },
// Additional types from old DB
...ADDITIONAL_EQUIP_TYPES.map((t, i) => ({
code: t.code,
name: t.name,
color: t.color,
order: 6 + i,
})),
],
});
// ===== REVISION TYPES =====
console.log('Creating revision types...');
await prisma.revisionType.createMany({
skipDuplicates: true,
data: [
{ code: 'MONTHLY', name: 'Mesačná revízia', intervalDays: 30, reminderDays: 7, color: '#6366F1', order: 1 },
{ code: 'QUARTERLY', name: 'Štvrťročná revízia', intervalDays: 90, reminderDays: 14, color: '#FFA500', order: 2 },
{ code: 'BIANNUAL', name: 'Polročná revízia', intervalDays: 180, reminderDays: 21, color: '#FBBF24', order: 3 },
{ code: 'ANNUAL', name: 'Ročná revízia', intervalDays: 365, reminderDays: 30, color: '#DC2626', order: 4 },
{ code: 'BIENNIAL', name: 'Revízia každé 2 roky', intervalDays: 730, reminderDays: 60, color: '#8B5CF6', order: 5 },
{ code: 'TRIENNIAL', name: 'Revízia každé 3 roky', intervalDays: 1095, reminderDays: 90, color: '#0EA5E9', order: 6 },
{ code: 'QUADRENNIAL', name: 'Revízia každé 4 roky', intervalDays: 1460, reminderDays: 90, color: '#14B8A6', order: 7 },
{ code: 'QUINQUENNIAL', name: 'Revízia každých 5 rokov', intervalDays: 1825, reminderDays: 120, color: '#F97316', order: 8 },
{ code: 'EMERGENCY', name: 'Mimoriadna revízia', intervalDays: 0, reminderDays: 0, color: '#EF4444', order: 9 },
],
});
// ===== RMA STATUSES =====
console.log('Creating RMA statuses...');
await prisma.rMAStatus.createMany({
skipDuplicates: true,
data: [
{ code: 'NEW', name: 'Nová reklamácia', color: '#10B981', isInitial: true, canTransitionTo: ['IN_ASSESSMENT', 'REJECTED'], order: 1 },
{ code: 'IN_ASSESSMENT', name: 'V posúdzovaní', color: '#F59E0B', canTransitionTo: ['APPROVED', 'REJECTED'], order: 2 },
{ code: 'APPROVED', name: 'Schválená', color: '#3B82F6', canTransitionTo: ['IN_REPAIR', 'REPLACED', 'REFUNDED'], order: 3 },
{ code: 'REJECTED', name: 'Zamietnutá', color: '#EF4444', isFinal: true, order: 4 },
{ code: 'IN_REPAIR', name: 'V oprave', color: '#8B5CF6', canTransitionTo: ['REPAIRED', 'COMPLETED'], order: 5 },
{ code: 'REPAIRED', name: 'Opravené', color: '#059669', canTransitionTo: ['COMPLETED'], order: 6 },
{ code: 'REPLACED', name: 'Vymenené', color: '#059669', canTransitionTo: ['COMPLETED'], order: 7 },
{ code: 'REFUNDED', name: 'Vrátené peniaze', color: '#059669', canTransitionTo: ['COMPLETED'], order: 8 },
{ code: 'COMPLETED', name: 'Uzatvorená', color: '#059669', isFinal: true, order: 9 },
],
});
// ===== RMA SOLUTIONS =====
console.log('Creating RMA solutions...');
await prisma.rMASolution.createMany({
skipDuplicates: true,
data: [
{ code: 'ASSESSMENT', name: 'Posúdzovanie', color: '#F59E0B', order: 1 },
{ code: 'REPAIR', name: 'Oprava', color: '#3B82F6', order: 2 },
{ code: 'REPLACEMENT', name: 'Výmena', color: '#10B981', order: 3 },
{ code: 'REFUND', name: 'Vrátenie peňazí', color: '#8B5CF6', order: 4 },
{ code: 'REJECTED', name: 'Zamietnutie', color: '#EF4444', order: 5 },
{ code: 'OTHER', name: 'Iné riešenie', color: '#6B7280', order: 6 },
],
});
// ===== TASK STATUSES =====
console.log('Creating task statuses...');
await prisma.taskStatus.createMany({
skipDuplicates: true,
data: [
{ code: 'NEW', name: 'Nová úloha', swimlaneColumn: 'NEW', color: '#10B981', isInitial: true, order: 1 },
{ code: 'IN_PROGRESS', name: 'V riešení', swimlaneColumn: 'DOING', color: '#F59E0B', order: 2 },
{ code: 'REVIEW', name: 'Na kontrolu', swimlaneColumn: 'DOING', color: '#8B5CF6', order: 3 },
{ code: 'COMPLETED', name: 'Dokončená', swimlaneColumn: 'DONE', color: '#059669', isFinal: true, order: 4 },
],
});
// ===== PRIORITIES =====
console.log('Creating priorities...');
await prisma.priority.createMany({
skipDuplicates: true,
data: [
{ code: 'LOW', name: 'Nízka priorita', color: '#10B981', level: 1, order: 1 },
{ code: 'MEDIUM', name: 'Stredná priorita', color: '#F59E0B', level: 5, order: 2 },
{ code: 'HIGH', name: 'Vysoká priorita', color: '#EF4444', level: 8, order: 3 },
{ code: 'URGENT', name: 'Urgentná', color: '#DC2626', level: 10, order: 4 },
],
});
// ===== SYSTEM SETTINGS =====
console.log('Creating system settings...');
await prisma.systemSetting.createMany({
skipDuplicates: true,
data: [
{
key: 'REVISION_REMINDER_DAYS',
value: 14,
category: 'NOTIFICATIONS',
label: 'Pripomenúť revíziu X dní dopredu',
dataType: 'number',
validation: { min: 1, max: 365 },
},
{
key: 'RMA_NUMBER_FORMAT',
value: 'RMA-{YYYY}{MM}{DD}{XXX}',
category: 'RMA',
label: 'Formát RMA čísla',
dataType: 'string',
},
{
key: 'RMA_CUSTOMER_REQUIRES_APPROVAL',
value: true,
category: 'RMA',
label: 'Reklamácie od zákazníkov vyžadujú schválenie',
dataType: 'boolean',
},
{
key: 'ADMIN_NOTIFICATION_EMAILS',
value: ['admin@firma.sk'],
category: 'NOTIFICATIONS',
label: 'Email adresy pre admin notifikácie',
dataType: 'json',
},
{
key: 'ENABLE_WEBSOCKET',
value: false,
category: 'GENERAL',
label: 'Zapnúť real-time aktualizácie (WebSocket)',
dataType: 'boolean',
},
{
key: 'NOTIFICATION_SNOOZE_OPTIONS',
value: [
{ label: '30 minút', minutes: 30 },
{ label: '1 hodina', minutes: 60 },
{ label: '3 hodiny', minutes: 180 },
{ label: 'Dnes poobede o 13:00', type: 'today', hour: 13 },
{ label: 'Zajtra ráno o 8:00', type: 'tomorrow', hour: 8 },
{ label: 'Zajtra poobede o 13:00', type: 'tomorrow', hour: 13 },
],
category: 'NOTIFICATIONS',
label: 'Možnosti odloženia notifikácií',
description: 'Pole objektov. Každý má "label" a buď "minutes" (relatívny čas) alebo "type" + "hour" (konkrétny čas). Type: "today" (ak čas prešiel, skryje sa), "tomorrow".',
dataType: 'json',
},
{
key: 'REVISION_STATUS_THRESHOLDS',
value: [
{ days: 30, label: 'Blíži sa', color: '#EAB308' },
{ days: 14, label: 'Blíži sa!', color: '#F97316' },
{ days: 7, label: 'Urgentné!', color: '#EF4444' },
],
category: 'REVISIONS',
label: 'Prahy upozornení revízií',
description: 'Pole prahov zoradených od najväčšieho po najmenší počet dní. Každý prah má: "days" (počet dní pred termínom), "label" (text), "color" (hex farba). Po termíne sa vždy zobrazuje červená.',
dataType: 'json',
},
],
});
// ===== USERS (demo + real from old DB) =====
console.log('Creating users...');
const rootRole = roles.find(r => r.code === 'ROOT');
const adminRole = roles.find(r => r.code === 'ADMIN');
const userRole = roles.find(r => r.code === 'USER');
const customerRole = roles.find(r => r.code === 'CUSTOMER');
if (!rootRole || !adminRole || !userRole || !customerRole) {
throw new Error('Required roles not found');
}
const [hashedPassword, hashedRoot, hashedAdmin, hashedUser, hashedZakaznik] = await Promise.all([
bcrypt.hash('heslo123', 10),
bcrypt.hash('root123', 10),
bcrypt.hash('admin123', 10),
bcrypt.hash('user123', 10),
bcrypt.hash('zakaznik123', 10),
]);
const rootUser = await prisma.user.upsert({
where: { email: 'root@helpdesk.sk' },
update: {},
create: {
email: 'root@helpdesk.sk',
password: hashedPassword,
name: 'Root Admin',
roleId: rootRole.id,
},
});
// Demo users - jeden pre každú rolu
const [demoRoot, demoAdmin, user1, user2, user3, demoZakaznik] = await Promise.all([
prisma.user.upsert({
where: { email: 'root@root.sk' },
update: {},
create: {
email: 'root@root.sk',
password: hashedRoot,
name: 'Root Používateľ',
roleId: rootRole.id,
},
}),
prisma.user.upsert({
where: { email: 'admin@admin.sk' },
update: {},
create: {
email: 'admin@admin.sk',
password: hashedAdmin,
name: 'Admin Používateľ',
roleId: adminRole.id,
},
}),
prisma.user.upsert({
where: { email: 'user1@user.sk' },
update: {},
create: {
email: 'user1@user.sk',
password: hashedUser,
name: 'Ján Technik',
roleId: userRole.id,
},
}),
prisma.user.upsert({
where: { email: 'user2@user.sk' },
update: {},
create: {
email: 'user2@user.sk',
password: hashedUser,
name: 'Peter Sieťar',
roleId: userRole.id,
},
}),
prisma.user.upsert({
where: { email: 'user3@user.sk' },
update: {},
create: {
email: 'user3@user.sk',
password: hashedUser,
name: 'Marek Montážnik',
roleId: userRole.id,
},
}),
prisma.user.upsert({
where: { email: 'zakaznik@zakaznik.sk' },
update: {},
create: {
email: 'zakaznik@zakaznik.sk',
password: hashedZakaznik,
name: 'Zákazník Demo',
roleId: customerRole.id,
},
}),
]);
console.log(' Created demo users: root@root.sk, admin@admin.sk, user1-3@user.sk, zakaznik@zakaznik.sk');
// All service records are assigned to root user
const defaultUserId = rootUser.id;
// ===== CUSTOMERS FROM OLD DB =====
console.log('Creating customers from old DB...');
const customerMap: Record<number, string> = {};
for (const c of CUSTOMERS) {
const customer = await prisma.customer.upsert({
where: { externalId: String(c.oldId) },
update: {},
create: {
name: c.name,
ico: c.ico && c.ico !== '0' ? c.ico : undefined,
dic: c.dic && c.dic !== '0' ? c.dic : undefined,
address: c.address,
email: c.email,
contactPerson: c.contactPerson,
contactPhone: c.phone,
externalId: String(c.oldId),
externalSource: 'old-helpdesk',
createdById: rootUser.id,
},
});
customerMap[c.oldId] = customer.id;
}
console.log(` Created ${Object.keys(customerMap).length} customers`);
// ===== ANALYZE SERVICE RECORDS =====
// Zistiť pre každé zariadenie: aké typy revízií má a kedy bola východzia revízia
console.log('Analyzing service records for revision metadata...');
interface EquipMeta {
typeCodes: Set<string>;
vychodziaDate: string | null;
}
const equipMeta: Map<number, EquipMeta> = new Map();
for (const [auditId, date, , , note] of SERVICE_RECORDS) {
const audit = AUDIT_MAP[auditId];
if (!audit) continue;
const hwId = audit.equipmentHwId;
if (!equipMeta.has(hwId)) {
equipMeta.set(hwId, { typeCodes: new Set(), vychodziaDate: null });
}
const meta = equipMeta.get(hwId)!;
// Klasifikovať typ revízie z poznámky
const typeCode = classifyNote(note, audit.periodicity);
meta.typeCodes.add(typeCode);
// Detekovať dátum "Východzej revízie"
if (isVychodziaNote(note) && !meta.vychodziaDate) {
meta.vychodziaDate = date;
}
}
console.log(` Found revision metadata for ${equipMeta.size} equipment items`);
for (const [hwId, meta] of equipMeta) {
if (meta.vychodziaDate) {
console.log(` hwId ${hwId}: východzia=${meta.vychodziaDate}, types=[${[...meta.typeCodes].join(', ')}]`);
}
}
// ===== EQUIPMENT FROM OLD DB =====
console.log('Creating equipment from old DB...');
// Fetch equipment type IDs
const equipTypes = await prisma.equipmentType.findMany();
const equipTypeMap: Record<string, string> = {};
for (const t of equipTypes) {
equipTypeMap[t.code] = t.id;
}
const equipmentIdMap: Record<number, string> = {}; // hwId -> new ID
for (const e of EQUIPMENT) {
const typeId = equipTypeMap[e.typeCode] || equipTypeMap['OTHER'];
const customerId = customerMap[e.companyId] || undefined;
const meta = equipMeta.get(e.hwId);
const equip = await prisma.equipment.create({
data: {
name: e.name,
typeId,
brand: e.brand || undefined,
customerId,
address: e.location || '-',
location: e.location || undefined,
partNumber: e.partNumber && !['-', ' ', '', '0'].includes(e.partNumber.trim()) ? e.partNumber : undefined,
serialNumber: e.serialNumber && !['-', ' ', '', '0', '.', '..', '...', ':', ',', '.,', ',.', ',,', '.-', '---', '-----', '-,'].includes(e.serialNumber.trim()) ? e.serialNumber : undefined,
description: e.description && !['-', ' ', '', 'ok'].includes(e.description.trim()) ? e.description : undefined,
installDate: e.installDate ? new Date(e.installDate) : undefined,
revisionCycleStart: meta?.vychodziaDate ? new Date(meta.vychodziaDate) : undefined,
createdById: rootUser.id,
},
});
equipmentIdMap[e.hwId] = equip.id;
}
console.log(` Created ${Object.keys(equipmentIdMap).length} equipment items`);
// ===== EQUIPMENT REVISION SCHEDULES =====
// Pre každé zariadenie vytvoriť záznamy o tom, aké typy revízií má priradené
console.log('Creating equipment revision schedules...');
const revisionTypes = await prisma.revisionType.findMany();
const revTypeMap: Record<string, string> = {};
for (const t of revisionTypes) {
revTypeMap[t.code] = t.id;
}
let scheduleCount = 0;
for (const [hwId, meta] of equipMeta) {
const equipId = equipmentIdMap[hwId];
if (!equipId) continue;
for (const typeCode of meta.typeCodes) {
const revTypeId = revTypeMap[typeCode];
if (!revTypeId) continue;
await prisma.equipmentRevisionSchedule.create({
data: {
equipmentId: equipId,
revisionTypeId: revTypeId,
},
});
scheduleCount++;
}
}
console.log(` Created ${scheduleCount} equipment revision schedules`);
// ===== REVISION RECORDS FROM OLD DB =====
console.log('Creating revision records from old DB...');
let createdRevisions = 0;
let skippedRevisions = 0;
// Deduplicate: same equipment + type + date = duplicate in old data
const seen = new Set<string>();
const uniqueRevisions = SERVICE_RECORDS
.map(([auditId, date, nextDate, _userEmail, note]) => {
const audit = AUDIT_MAP[auditId];
if (!audit) return null;
const equipId = equipmentIdMap[audit.equipmentHwId];
if (!equipId) return null;
// Klasifikovať typ revízie podľa poznámky (nie len podľa audit periodicity)
const typeCode = classifyNote(note, audit.periodicity);
const typeId = revTypeMap[typeCode];
if (!typeId) return null;
// Deduplicate by equipment + type + date
const key = `${equipId}-${typeId}-${date}`;
if (seen.has(key)) return null;
seen.add(key);
return {
equipmentId: equipId,
typeId,
performedDate: new Date(date),
nextDueDate: nextDate ? new Date(nextDate) : null,
performedById: defaultUserId,
notes: note && note.trim() ? note.trim() : undefined,
result: 'OK',
};
})
.filter((r): r is NonNullable<typeof r> => r !== null);
// Process in batches
const BATCH_SIZE = 50;
for (let i = 0; i < uniqueRevisions.length; i += BATCH_SIZE) {
const batch = uniqueRevisions.slice(i, i + BATCH_SIZE);
await prisma.revision.createMany({ data: batch });
createdRevisions += batch.length;
}
skippedRevisions = SERVICE_RECORDS.length - uniqueRevisions.length;
console.log(` Created ${createdRevisions} revisions (${skippedRevisions} duplicates skipped)`);
// ===== DEMO TASKS =====
console.log('Creating demo tasks...');
const statuses = await prisma.taskStatus.findMany();
const priorities = await prisma.priority.findMany();
const statusByCode = (code: string) => statuses.find(s => s.code === code)!.id;
const priorityByCode = (code: string) => priorities.find(p => p.code === code)!.id;
const demoTasks = [
// --- Admin zadáva úlohy pre userov ---
{
title: 'Objednať UTP káble Cat6a (100m)',
description: 'Objednať 3 balenia UTP káblov Cat6a po 100m pre nový serverový rack. Dodávateľ: Senetic alebo TS Bohemia.',
statusCode: 'NEW',
priorityCode: 'MEDIUM',
createdById: demoAdmin.id,
assignees: [user1.id],
deadline: new Date('2026-03-10'),
},
{
title: 'Výmena UPS batérií v racku R3',
description: 'APC Smart-UPS 1500VA - batérie sú po záruke, hlási "Replace Battery". Objednať RBC7 a vymeniť.',
statusCode: 'NEW',
priorityCode: 'URGENT',
createdById: demoAdmin.id,
assignees: [user2.id],
deadline: new Date('2026-02-28'),
},
{
title: 'Inštalácia kamerového systému - vstupná hala',
description: 'Namontovať 2x Hikvision DS-2CD2143G2 + nastaviť NVR záznam na 30 dní. Kabeláž už je pripravená.',
statusCode: 'IN_PROGRESS',
priorityCode: 'MEDIUM',
createdById: demoAdmin.id,
assignees: [user3.id],
deadline: new Date('2026-03-15'),
},
{
title: 'Oprava Wi-Fi pokrytia v zasadačke B2',
description: 'Zákazník hlási slabý signál. Skontrolovať site survey, prípadne pridať ďalší AP alebo presunúť existujúci.',
statusCode: 'NEW',
priorityCode: 'HIGH',
createdById: demoAdmin.id,
assignees: [user1.id, user2.id],
deadline: new Date('2026-03-08'),
},
{
title: 'Objednať toner do tlačiarne HP LaserJet Pro',
description: 'Kancelária 3. poschodie - toner CF259X je takmer prázdny. Objednať 2 kusy na sklad.',
statusCode: 'COMPLETED',
priorityCode: 'LOW',
createdById: demoAdmin.id,
assignees: [user1.id],
},
// --- Root zadáva úlohy pre userov a admina ---
{
title: 'Nastaviť switch Cisco SG350 v serverovni',
description: 'Konfigurácia VLAN 10, 20, 30. Trunk port na uplink. Nastaviť SNMP monitoring a port security na access portoch.',
statusCode: 'IN_PROGRESS',
priorityCode: 'HIGH',
createdById: demoRoot.id,
assignees: [demoAdmin.id, user1.id],
deadline: new Date('2026-03-05'),
},
{
title: 'Nastaviť VPN tunel medzi pobočkami',
description: 'IPSec site-to-site VPN medzi Bratislava a Košice. MikroTik RB4011 na oboch stranách. Zdieľaný subnet 10.10.0.0/24.',
statusCode: 'IN_PROGRESS',
priorityCode: 'HIGH',
createdById: demoRoot.id,
assignees: [user2.id, user3.id],
deadline: new Date('2026-03-01'),
},
{
title: 'Migrácia mailboxov na Microsoft 365',
description: 'Presunúť 25 mailboxov z on-prem Exchange na M365. Pripraviť migračný plán, otestovať na 3 pilotných užívateľoch.',
statusCode: 'NEW',
priorityCode: 'MEDIUM',
createdById: demoRoot.id,
assignees: [demoAdmin.id, user1.id, user2.id],
deadline: new Date('2026-04-01'),
},
// --- Úlohy kde admin robí sám ---
{
title: 'Zálohovanie konfigurácie sieťových zariadení',
description: 'Exportovať running-config zo všetkých switchov a routerov. Uložiť do Git repozitára na NAS.',
statusCode: 'COMPLETED',
priorityCode: 'MEDIUM',
createdById: demoAdmin.id,
assignees: [demoAdmin.id],
},
{
title: 'Aktualizovať firmware na AP Ubiquiti U6-Pro',
description: 'Nová verzia firmware opravuje bug s roamingom klientov. Aktualizovať všetkých 8 AP cez UniFi Controller.',
statusCode: 'REVIEW',
priorityCode: 'LOW',
createdById: demoAdmin.id,
assignees: [demoAdmin.id, user3.id],
},
// --- Root + admin spoločne ---
{
title: 'Audit sieťovej infraštruktúry',
description: 'Kompletný audit všetkých aktívnych prvkov, kontrola firmware verzií, kontrola prístupových práv na zariadeniach.',
statusCode: 'NEW',
priorityCode: 'MEDIUM',
createdById: demoRoot.id,
assignees: [demoAdmin.id],
deadline: new Date('2026-03-20'),
},
// --- User si vytvára vlastnú úlohu ---
{
title: 'Zdokumentovať zapojenie patch panelov v racku R1',
description: 'Vytvoriť schému zapojenia patch panelov a označiť káble podľa štandardu TIA-606.',
statusCode: 'IN_PROGRESS',
priorityCode: 'LOW',
createdById: user2.id,
assignees: [user2.id],
},
];
for (const t of demoTasks) {
const task = await prisma.task.create({
data: {
title: t.title,
description: t.description,
statusId: statusByCode(t.statusCode),
priorityId: priorityByCode(t.priorityCode),
createdById: t.createdById,
deadline: t.deadline,
completedAt: t.statusCode === 'COMPLETED' ? new Date() : undefined,
},
});
if (t.assignees.length > 0) {
await prisma.taskAssignee.createMany({
data: t.assignees.map(userId => ({
taskId: task.id,
userId,
})),
});
}
}
console.log(` Created ${demoTasks.length} demo tasks`);
console.log('Seeding completed!');
}
seed()
.catch((error) => {
console.error('Seeding failed:', error);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});