mirror of
https://github.com/donpat1to/Schichtenplaner.git
synced 2025-12-01 06:55:45 +01:00
Compare commits
3 Commits
3ad497dd76
...
00b48c1f41
| Author | SHA1 | Date | |
|---|---|---|---|
| 00b48c1f41 | |||
| cae2b83649 | |||
| a69e934075 |
@@ -4,7 +4,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run build && npx tsx src/server.ts",
|
"dev": "npm run build && npx tsx src/server.ts",
|
||||||
"build": "tsc",
|
"dev:single": "cross-env NODE_ENV=development npx tsx src/server.ts",
|
||||||
|
"build": "tsc",
|
||||||
"start": "node dist/server.js",
|
"start": "node dist/server.js",
|
||||||
"prestart": "npm run build",
|
"prestart": "npm run build",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
"@types/jest": "^29.5.0",
|
"@types/jest": "^29.5.0",
|
||||||
"ts-node": "^10.9.0",
|
"ts-node": "^10.9.0",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"tsx": "^4.0.0"
|
"tsx": "^4.0.0",
|
||||||
|
"cross-env": "10.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,10 +33,10 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
|
|
||||||
console.log(`✅ Using schema at: ${schemaPath}`);
|
console.log(`✅ Using schema at: ${schemaPath}`);
|
||||||
const schema = fs.readFileSync(schemaPath, 'utf8');
|
const schema = fs.readFileSync(schemaPath, 'utf8');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Starting database initialization...');
|
console.log('Starting database initialization...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const existingAdmin = await db.get<{ count: number }>(
|
const existingAdmin = await db.get<{ count: number }>(
|
||||||
`SELECT COUNT(*) as count
|
`SELECT COUNT(*) as count
|
||||||
@@ -44,7 +44,7 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
JOIN employee_roles er ON e.id = er.employee_id
|
JOIN employee_roles er ON e.id = er.employee_id
|
||||||
WHERE er.role = 'admin' AND e.is_active = 1`
|
WHERE er.role = 'admin' AND e.is_active = 1`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (existingAdmin && existingAdmin.count > 0) {
|
if (existingAdmin && existingAdmin.count > 0) {
|
||||||
console.log('✅ Database already initialized with admin user');
|
console.log('✅ Database already initialized with admin user');
|
||||||
return;
|
return;
|
||||||
@@ -52,23 +52,23 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('ℹ️ Database tables might not exist yet, creating schema...');
|
console.log('ℹ️ Database tables might not exist yet, creating schema...');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get list of existing tables
|
// Get list of existing tables
|
||||||
interface TableInfo {
|
interface TableInfo {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const existingTables = await db.all<TableInfo>(
|
const existingTables = await db.all<TableInfo>(
|
||||||
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('Existing tables found:', existingTables.map(t => t.name).join(', ') || 'none');
|
console.log('Existing tables found:', existingTables.map(t => t.name).join(', ') || 'none');
|
||||||
|
|
||||||
// UPDATED: Drop tables in correct dependency order for new schema
|
// Drop tables in correct dependency order for new schema
|
||||||
const tablesToDrop = [
|
const tablesToDrop = [
|
||||||
'employee_availability',
|
'employee_availability',
|
||||||
'shift_assignments',
|
'shift_assignments',
|
||||||
'scheduled_shifts',
|
'scheduled_shifts',
|
||||||
'shifts',
|
'shifts',
|
||||||
'time_slots',
|
'time_slots',
|
||||||
@@ -79,7 +79,7 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
'shift_plans',
|
'shift_plans',
|
||||||
'applied_migrations'
|
'applied_migrations'
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const table of tablesToDrop) {
|
for (const table of tablesToDrop) {
|
||||||
if (existingTables.some(t => t.name === table)) {
|
if (existingTables.some(t => t.name === table)) {
|
||||||
console.log(`Dropping table: ${table}`);
|
console.log(`Dropping table: ${table}`);
|
||||||
@@ -94,17 +94,41 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
console.error('Error checking/dropping existing tables:', error);
|
console.error('Error checking/dropping existing tables:', error);
|
||||||
// Continue with schema creation even if table dropping fails
|
// Continue with schema creation even if table dropping fails
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute schema creation in a transaction
|
// NEU: PRAGMA-Anweisungen außerhalb der Transaktion ausführen
|
||||||
await db.run('BEGIN EXCLUSIVE TRANSACTION');
|
console.log('Executing PRAGMA statements outside transaction...');
|
||||||
|
const pragmaStatements = schema
|
||||||
// Execute each statement separately for better error reporting
|
|
||||||
const statements = schema
|
|
||||||
.split(';')
|
.split(';')
|
||||||
.map(stmt => stmt.trim())
|
.map(stmt => stmt.trim())
|
||||||
.filter(stmt => stmt.length > 0)
|
.filter(stmt => stmt.length > 0)
|
||||||
|
.filter(stmt => stmt.toUpperCase().startsWith('PRAGMA'))
|
||||||
|
.map(stmt => {
|
||||||
|
return stmt.split('\n')
|
||||||
|
.filter(line => !line.trim().startsWith('--'))
|
||||||
|
.join('\n')
|
||||||
|
.trim();
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const statement of pragmaStatements) {
|
||||||
|
try {
|
||||||
|
console.log('Executing PRAGMA:', statement);
|
||||||
|
await db.run(statement);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('PRAGMA statement might have failed:', statement, error);
|
||||||
|
// Continue even if PRAGMA fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema-Erstellung in Transaktion
|
||||||
|
await db.run('BEGIN EXCLUSIVE TRANSACTION');
|
||||||
|
|
||||||
|
// Nur die CREATE TABLE und andere Anweisungen (ohne PRAGMA)
|
||||||
|
const schemaStatements = schema
|
||||||
|
.split(';')
|
||||||
|
.map(stmt => stmt.trim())
|
||||||
|
.filter(stmt => stmt.length > 0)
|
||||||
|
.filter(stmt => !stmt.toUpperCase().startsWith('PRAGMA'))
|
||||||
.map(stmt => {
|
.map(stmt => {
|
||||||
// Remove any single-line comments
|
|
||||||
return stmt.split('\n')
|
return stmt.split('\n')
|
||||||
.filter(line => !line.trim().startsWith('--'))
|
.filter(line => !line.trim().startsWith('--'))
|
||||||
.join('\n')
|
.join('\n')
|
||||||
@@ -112,7 +136,7 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
})
|
})
|
||||||
.filter(stmt => stmt.length > 0);
|
.filter(stmt => stmt.length > 0);
|
||||||
|
|
||||||
for (const statement of statements) {
|
for (const statement of schemaStatements) {
|
||||||
try {
|
try {
|
||||||
console.log('Executing statement:', statement.substring(0, 50) + '...');
|
console.log('Executing statement:', statement.substring(0, 50) + '...');
|
||||||
await db.run(statement);
|
await db.run(statement);
|
||||||
@@ -123,8 +147,8 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UPDATED: Insert default data in correct order
|
// Insert default data in correct order
|
||||||
try {
|
try {
|
||||||
console.log('Inserting default employee types...');
|
console.log('Inserting default employee types...');
|
||||||
await db.run(`INSERT OR IGNORE INTO employee_types (type, category, has_contract_type) VALUES ('manager', 'internal', 1)`);
|
await db.run(`INSERT OR IGNORE INTO employee_types (type, category, has_contract_type) VALUES ('manager', 'internal', 1)`);
|
||||||
@@ -132,7 +156,7 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
await db.run(`INSERT OR IGNORE INTO employee_types (type, category, has_contract_type) VALUES ('apprentice', 'internal', 1)`);
|
await db.run(`INSERT OR IGNORE INTO employee_types (type, category, has_contract_type) VALUES ('apprentice', 'internal', 1)`);
|
||||||
await db.run(`INSERT OR IGNORE INTO employee_types (type, category, has_contract_type) VALUES ('guest', 'external', 0)`);
|
await db.run(`INSERT OR IGNORE INTO employee_types (type, category, has_contract_type) VALUES ('guest', 'external', 0)`);
|
||||||
console.log('✅ Default employee types inserted');
|
console.log('✅ Default employee types inserted');
|
||||||
|
|
||||||
console.log('Inserting default roles...');
|
console.log('Inserting default roles...');
|
||||||
await db.run(`INSERT OR IGNORE INTO roles (role, authority_level, description) VALUES ('admin', 100, 'Vollzugriff')`);
|
await db.run(`INSERT OR IGNORE INTO roles (role, authority_level, description) VALUES ('admin', 100, 'Vollzugriff')`);
|
||||||
await db.run(`INSERT OR IGNORE INTO roles (role, authority_level, description) VALUES ('maintenance', 50, 'Wartungszugriff')`);
|
await db.run(`INSERT OR IGNORE INTO roles (role, authority_level, description) VALUES ('maintenance', 50, 'Wartungszugriff')`);
|
||||||
@@ -143,13 +167,13 @@ export async function initializeDatabase(): Promise<void> {
|
|||||||
await db.run('ROLLBACK');
|
await db.run('ROLLBACK');
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.run('COMMIT');
|
await db.run('COMMIT');
|
||||||
console.log('✅ Database schema successfully initialized');
|
console.log('✅ Database schema successfully initialized');
|
||||||
|
|
||||||
// Give a small delay to ensure all transactions are properly closed
|
// Give a small delay to ensure all transactions are properly closed
|
||||||
await new Promise(resolve => setTimeout(resolve, 100));
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during database initialization:', error);
|
console.error('Error during database initialization:', error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
|
|||||||
import { initializeDatabase } from './scripts/initializeDatabase.js';
|
import { initializeDatabase } from './scripts/initializeDatabase.js';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
|
import type { ViteDevServer } from 'vite';
|
||||||
|
|
||||||
// Route imports
|
// Route imports
|
||||||
import authRoutes from './routes/auth.js';
|
import authRoutes from './routes/auth.js';
|
||||||
@@ -22,6 +23,62 @@ const app = express();
|
|||||||
const PORT = 3002;
|
const PORT = 3002;
|
||||||
const isDevelopment = process.env.NODE_ENV === 'development';
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
||||||
|
|
||||||
|
let vite: ViteDevServer | undefined;
|
||||||
|
|
||||||
|
if (isDevelopment) {
|
||||||
|
// Dynamically import and setup Vite middleware
|
||||||
|
const setupViteDevServer = async () => {
|
||||||
|
try {
|
||||||
|
const { createServer } = await import('vite');
|
||||||
|
vite = await createServer({
|
||||||
|
server: { middlewareMode: true },
|
||||||
|
appType: 'spa'
|
||||||
|
});
|
||||||
|
app.use(vite.middlewares);
|
||||||
|
console.log('🔧 Vite dev server integrated with Express');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('⚠️ Vite integration failed, using static files:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setupViteDevServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
const configureStaticFiles = () => {
|
||||||
|
const possiblePaths = [
|
||||||
|
// Production path (Docker)
|
||||||
|
'/app/frontend-build',
|
||||||
|
// Development paths
|
||||||
|
path.resolve(__dirname, '../../frontend/dist'),
|
||||||
|
path.resolve(__dirname, '../frontend/dist'), // Added for monorepo
|
||||||
|
path.resolve(process.cwd(), 'frontend/dist'), // Current directory
|
||||||
|
path.resolve(process.cwd(), '../frontend/dist'), // Parent directory
|
||||||
|
// Vite dev server fallback
|
||||||
|
...(isDevelopment ? [path.resolve(__dirname, '../../frontend')] : [])
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const testPath of possiblePaths) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(testPath)) {
|
||||||
|
// In development, check for dist or direct source
|
||||||
|
if (fs.existsSync(path.join(testPath, 'index.html'))) {
|
||||||
|
console.log('✅ Found frontend at:', testPath);
|
||||||
|
app.use(express.static(testPath));
|
||||||
|
return testPath;
|
||||||
|
}
|
||||||
|
// For Vite dev server in development
|
||||||
|
else if (isDevelopment && fs.existsSync(path.join(testPath, 'index.html'))) {
|
||||||
|
console.log('🔧 Development: Serving frontend source from:', testPath);
|
||||||
|
app.use(express.static(testPath));
|
||||||
|
return testPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Silent catch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
app.set('trust proxy', true);
|
app.set('trust proxy', true);
|
||||||
|
|
||||||
// Security configuration
|
// Security configuration
|
||||||
@@ -81,8 +138,8 @@ app.use('/api/scheduling', schedulingRoutes);
|
|||||||
|
|
||||||
// Health route
|
// Health route
|
||||||
app.get('/api/health', (req: express.Request, res: express.Response) => {
|
app.get('/api/health', (req: express.Request, res: express.Response) => {
|
||||||
res.json({
|
res.json({
|
||||||
status: 'OK',
|
status: 'OK',
|
||||||
message: 'Backend läuft!',
|
message: 'Backend läuft!',
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
mode: process.env.NODE_ENV || 'development'
|
mode: process.env.NODE_ENV || 'development'
|
||||||
@@ -118,6 +175,7 @@ const findFrontendBuildPath = (): string | null => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const frontendBuildPath = findFrontendBuildPath();
|
const frontendBuildPath = findFrontendBuildPath();
|
||||||
|
const staticPath = configureStaticFiles();
|
||||||
|
|
||||||
if (frontendBuildPath) {
|
if (frontendBuildPath) {
|
||||||
app.use(express.static(frontendBuildPath));
|
app.use(express.static(frontendBuildPath));
|
||||||
@@ -130,46 +188,71 @@ if (frontendBuildPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Root route
|
// Root route
|
||||||
app.get('/', (req, res) => {
|
app.get('/', async (req, res) => {
|
||||||
if (!frontendBuildPath) {
|
// In development with Vite middleware
|
||||||
if (isDevelopment) {
|
if (vite) {
|
||||||
return res.redirect('http://localhost:3003');
|
try {
|
||||||
|
const template = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, '../../frontend/index.html'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
const html = await vite.transformIndexHtml(req.url, template);
|
||||||
|
res.send(html);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).send('Vite dev server error');
|
||||||
}
|
}
|
||||||
return res.status(500).send('Frontend build not found');
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback to static file serving
|
||||||
|
if (!frontendBuildPath) {
|
||||||
|
return res.status(500).send('Frontend not available');
|
||||||
|
}
|
||||||
|
|
||||||
const indexPath = path.join(frontendBuildPath, 'index.html');
|
const indexPath = path.join(frontendBuildPath, 'index.html');
|
||||||
res.sendFile(indexPath);
|
res.sendFile(indexPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Client-side routing fallback
|
// Client-side routing fallback
|
||||||
app.get('*', (req, res) => {
|
app.get('*', async (req, res, next) => {
|
||||||
if (req.path.startsWith('/api/')) {
|
if (req.path.startsWith('/api/')) {
|
||||||
return res.status(404).json({ error: 'API endpoint not found' });
|
return next(); // API routes handled normally
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frontendBuildPath) {
|
// Vite dev server handling
|
||||||
if (isDevelopment) {
|
if (vite) {
|
||||||
return res.redirect(`http://localhost:3003${req.path}`);
|
try {
|
||||||
|
const template = fs.readFileSync(
|
||||||
|
path.resolve(__dirname, '../../frontend/index.html'),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
const html = await vite.transformIndexHtml(req.url, template);
|
||||||
|
return res.send(html);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Vite transformation error:', error);
|
||||||
}
|
}
|
||||||
return res.status(500).json({ error: 'Frontend application not available' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const indexPath = path.join(frontendBuildPath, 'index.html');
|
// Static file fallback
|
||||||
res.sendFile(indexPath);
|
if (staticPath) {
|
||||||
|
const indexPath = path.join(staticPath, 'index.html');
|
||||||
|
return res.sendFile(indexPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(500).send('Frontend not available');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Error handling
|
// Error handling
|
||||||
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
app.use((err: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
console.error('Error:', err);
|
console.error('Error:', err);
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
error: 'Internal server error',
|
error: 'Internal server error',
|
||||||
message: 'Something went wrong'
|
message: 'Something went wrong'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
error: 'Internal server error',
|
error: 'Internal server error',
|
||||||
message: err.message,
|
message: err.message,
|
||||||
stack: err.stack
|
stack: err.stack
|
||||||
@@ -196,7 +279,7 @@ const initializeApp = async () => {
|
|||||||
if (frontendBuildPath) {
|
if (frontendBuildPath) {
|
||||||
console.log(`📍 Frontend: http://localhost:${PORT}`);
|
console.log(`📍 Frontend: http://localhost:${PORT}`);
|
||||||
} else if (isDevelopment) {
|
} else if (isDevelopment) {
|
||||||
console.log(`📍 Frontend (Vite): http://localhost:3003`);
|
console.log(`📍 Frontend (Vite): http://localhost:3002`);
|
||||||
}
|
}
|
||||||
console.log(`📍 API: http://localhost:${PORT}/api`);
|
console.log(`📍 API: http://localhost:${PORT}/api`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ interface EmployeeFormData {
|
|||||||
lastname: string;
|
lastname: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
// Step 2: Mitarbeiterkategorie
|
// Step 2: Mitarbeiterkategorie
|
||||||
employeeType: EmployeeType;
|
employeeType: EmployeeType;
|
||||||
contractType: ContractType | undefined;
|
contractType: ContractType | undefined;
|
||||||
isTrainee: boolean;
|
isTrainee: boolean;
|
||||||
|
|
||||||
// Step 3: Berechtigungen & Status
|
// Step 3: Berechtigungen & Status
|
||||||
roles: string[];
|
roles: string[];
|
||||||
canWorkAlone: boolean;
|
canWorkAlone: boolean;
|
||||||
@@ -64,12 +64,12 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
canWorkAlone: false,
|
canWorkAlone: false,
|
||||||
isActive: true
|
isActive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const [passwordForm, setPasswordForm] = useState<PasswordFormData>({
|
const [passwordForm, setPasswordForm] = useState<PasswordFormData>({
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
confirmPassword: ''
|
confirmPassword: ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const [showPasswordSection, setShowPasswordSection] = useState(false);
|
const [showPasswordSection, setShowPasswordSection] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
@@ -116,7 +116,7 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
|
|
||||||
const cleanFirstname = convertUmlauts(firstname).replace(/[^a-z0-9]/g, '');
|
const cleanFirstname = convertUmlauts(firstname).replace(/[^a-z0-9]/g, '');
|
||||||
const cleanLastname = convertUmlauts(lastname).replace(/[^a-z0-9]/g, '');
|
const cleanLastname = convertUmlauts(lastname).replace(/[^a-z0-9]/g, '');
|
||||||
|
|
||||||
return `${cleanFirstname}.${cleanLastname}@sp.de`;
|
return `${cleanFirstname}.${cleanLastname}@sp.de`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
const goToNextStep = (): void => {
|
const goToNextStep = (): void => {
|
||||||
setError('');
|
setError('');
|
||||||
clearErrors(); // Clear previous validation errors
|
clearErrors(); // Clear previous validation errors
|
||||||
|
|
||||||
if (!validateCurrentStep(currentStep)) {
|
if (!validateCurrentStep(currentStep)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
const handleStepChange = (stepIndex: number): void => {
|
const handleStepChange = (stepIndex: number): void => {
|
||||||
setError('');
|
setError('');
|
||||||
clearErrors(); // Clear validation errors when changing steps
|
clearErrors(); // Clear validation errors when changing steps
|
||||||
|
|
||||||
// Nur erlauben, zu bereits validierten Schritten zu springen
|
// Nur erlauben, zu bereits validierten Schritten zu springen
|
||||||
if (stepIndex <= currentStep + 1) {
|
if (stepIndex <= currentStep + 1) {
|
||||||
// Vor dem Wechsel validieren
|
// Vor dem Wechsel validieren
|
||||||
@@ -212,7 +212,7 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
// ===== FORM HANDLER =====
|
// ===== FORM HANDLER =====
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||||
const { name, value, type } = e.target;
|
const { name, value, type } = e.target;
|
||||||
|
|
||||||
setFormData(prev => ({
|
setFormData(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
[name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value
|
[name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value
|
||||||
@@ -264,9 +264,9 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Determine if can work alone based on employee type
|
// Determine if can work alone based on employee type
|
||||||
const canWorkAlone = employeeType === 'manager' ||
|
const canWorkAlone = employeeType === 'manager' ||
|
||||||
(employeeType === 'personell' && !formData.isTrainee);
|
(employeeType === 'personell' && !formData.isTrainee);
|
||||||
|
|
||||||
// Reset isTrainee if not personell
|
// Reset isTrainee if not personell
|
||||||
const isTrainee = employeeType === 'personell' ? formData.isTrainee : false;
|
const isTrainee = employeeType === 'personell' ? formData.isTrainee : false;
|
||||||
|
|
||||||
@@ -311,9 +311,9 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
canWorkAlone: formData.canWorkAlone,
|
canWorkAlone: formData.canWorkAlone,
|
||||||
isTrainee: formData.isTrainee
|
isTrainee: formData.isTrainee
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use executeWithValidation ONLY for the API call
|
// Use executeWithValidation ONLY for the API call
|
||||||
await executeWithValidation(() =>
|
await executeWithValidation(() =>
|
||||||
employeeService.createEmployee(createData)
|
employeeService.createEmployee(createData)
|
||||||
);
|
);
|
||||||
} else if (employee) {
|
} else if (employee) {
|
||||||
@@ -327,9 +327,9 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
isActive: formData.isActive,
|
isActive: formData.isActive,
|
||||||
isTrainee: formData.isTrainee
|
isTrainee: formData.isTrainee
|
||||||
};
|
};
|
||||||
|
|
||||||
// Use executeWithValidation for the update call
|
// Use executeWithValidation for the update call
|
||||||
await executeWithValidation(() =>
|
await executeWithValidation(() =>
|
||||||
employeeService.updateEmployee(employee.id, updateData)
|
employeeService.updateEmployee(employee.id, updateData)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -343,12 +343,13 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
await executeWithValidation(() =>
|
await executeWithValidation(() =>
|
||||||
employeeService.changePassword(employee.id, {
|
employeeService.changePassword(employee.id, {
|
||||||
currentPassword: '',
|
currentPassword: '',
|
||||||
newPassword: passwordForm.newPassword
|
newPassword: passwordForm.newPassword,
|
||||||
|
confirmPassword: passwordForm.confirmPassword
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// Only set error if it's not a validation error (validation errors are handled by the hook)
|
// Only set error if it's not a validation error (validation errors are handled by the hook)
|
||||||
@@ -364,9 +365,9 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
const isStepCompleted = (stepIndex: number): boolean => {
|
const isStepCompleted = (stepIndex: number): boolean => {
|
||||||
switch (stepIndex) {
|
switch (stepIndex) {
|
||||||
case 0:
|
case 0:
|
||||||
return !!formData.firstname.trim() &&
|
return !!formData.firstname.trim() &&
|
||||||
!!formData.lastname.trim();
|
!!formData.lastname.trim();
|
||||||
// REMOVE: (mode === 'edit' || formData.password.length >= 6)
|
// REMOVE: (mode === 'edit' || formData.password.length >= 6)
|
||||||
case 1:
|
case 1:
|
||||||
return !!formData.employeeType;
|
return !!formData.employeeType;
|
||||||
case 2:
|
case 2:
|
||||||
@@ -391,7 +392,7 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
validationErrors,
|
validationErrors,
|
||||||
getFieldError,
|
getFieldError,
|
||||||
hasErrors,
|
hasErrors,
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
goToNextStep,
|
goToNextStep,
|
||||||
goToPrevStep,
|
goToPrevStep,
|
||||||
@@ -405,7 +406,7 @@ const useEmployeeForm = (mode: 'create' | 'edit', employee?: Employee) => {
|
|||||||
handleSubmit,
|
handleSubmit,
|
||||||
setShowPasswordSection,
|
setShowPasswordSection,
|
||||||
clearErrors,
|
clearErrors,
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
isStepCompleted
|
isStepCompleted
|
||||||
};
|
};
|
||||||
@@ -430,8 +431,8 @@ interface StepContentProps {
|
|||||||
hasErrors: (fieldName?: string) => boolean;
|
hasErrors: (fieldName?: string) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Step1Content: React.FC<StepContentProps> = ({
|
const Step1Content: React.FC<StepContentProps> = ({
|
||||||
formData,
|
formData,
|
||||||
onInputChange,
|
onInputChange,
|
||||||
emailPreview,
|
emailPreview,
|
||||||
mode
|
mode
|
||||||
@@ -439,9 +440,9 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }}>
|
||||||
<div>
|
<div>
|
||||||
<label style={{
|
<label style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginBottom: '0.5rem',
|
marginBottom: '0.5rem',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
color: '#495057'
|
color: '#495057'
|
||||||
}}>
|
}}>
|
||||||
@@ -465,9 +466,9 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label style={{
|
<label style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginBottom: '0.5rem',
|
marginBottom: '0.5rem',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
color: '#495057'
|
color: '#495057'
|
||||||
}}>
|
}}>
|
||||||
@@ -493,17 +494,17 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
|
|
||||||
{/* Email Preview */}
|
{/* Email Preview */}
|
||||||
<div>
|
<div>
|
||||||
<label style={{
|
<label style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginBottom: '0.5rem',
|
marginBottom: '0.5rem',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
color: '#495057'
|
color: '#495057'
|
||||||
}}>
|
}}>
|
||||||
E-Mail Adresse (automatisch generiert)
|
E-Mail Adresse (automatisch generiert)
|
||||||
</label>
|
</label>
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '0.75rem',
|
padding: '0.75rem',
|
||||||
backgroundColor: '#e9ecef',
|
backgroundColor: '#e9ecef',
|
||||||
border: '1px solid #ced4da',
|
border: '1px solid #ced4da',
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
color: '#495057',
|
color: '#495057',
|
||||||
@@ -512,8 +513,8 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
}}>
|
}}>
|
||||||
{emailPreview || 'max.mustermann@sp.de'}
|
{emailPreview || 'max.mustermann@sp.de'}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
color: '#6c757d',
|
color: '#6c757d',
|
||||||
marginTop: '0.25rem'
|
marginTop: '0.25rem'
|
||||||
}}>
|
}}>
|
||||||
@@ -523,9 +524,9 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
|
|
||||||
{mode === 'create' && (
|
{mode === 'create' && (
|
||||||
<div>
|
<div>
|
||||||
<label style={{
|
<label style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
marginBottom: '0.5rem',
|
marginBottom: '0.5rem',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
color: '#495057'
|
color: '#495057'
|
||||||
}}>
|
}}>
|
||||||
@@ -546,8 +547,8 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
}}
|
}}
|
||||||
placeholder="Passwort eingeben"
|
placeholder="Passwort eingeben"
|
||||||
/>
|
/>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
color: '#6c757d',
|
color: '#6c757d',
|
||||||
marginTop: '0.25rem'
|
marginTop: '0.25rem'
|
||||||
}}>
|
}}>
|
||||||
@@ -558,7 +559,7 @@ const Step1Content: React.FC<StepContentProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Step2Content: React.FC<StepContentProps> = ({
|
const Step2Content: React.FC<StepContentProps> = ({
|
||||||
formData,
|
formData,
|
||||||
onEmployeeTypeChange,
|
onEmployeeTypeChange,
|
||||||
onTraineeChange,
|
onTraineeChange,
|
||||||
@@ -581,11 +582,11 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
{/* Mitarbeiter Kategorie */}
|
{/* Mitarbeiter Kategorie */}
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{ margin: '0 0 1rem 0', color: '#495057' }}>👥 Mitarbeiter Kategorie</h3>
|
<h3 style={{ margin: '0 0 1rem 0', color: '#495057' }}>👥 Mitarbeiter Kategorie</h3>
|
||||||
|
|
||||||
{employeeTypeError && (
|
{employeeTypeError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginBottom: '1rem',
|
marginBottom: '1rem',
|
||||||
padding: '0.5rem',
|
padding: '0.5rem',
|
||||||
backgroundColor: '#f8d7da',
|
backgroundColor: '#f8d7da',
|
||||||
@@ -595,10 +596,10 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
{employeeTypeError}
|
{employeeTypeError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
||||||
{Object.values(EMPLOYEE_TYPE_CONFIG).map(type => (
|
{Object.values(EMPLOYEE_TYPE_CONFIG).map(type => (
|
||||||
<div
|
<div
|
||||||
key={type.value}
|
key={type.value}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -626,16 +627,16 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#2c3e50',
|
color: '#2c3e50',
|
||||||
marginBottom: '4px',
|
marginBottom: '4px',
|
||||||
fontSize: '16px'
|
fontSize: '16px'
|
||||||
}}>
|
}}>
|
||||||
{type.label}
|
{type.label}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
color: '#7f8c8d',
|
color: '#7f8c8d',
|
||||||
lineHeight: '1.4'
|
lineHeight: '1.4'
|
||||||
}}>
|
}}>
|
||||||
@@ -658,10 +659,10 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
|
|
||||||
{/* Trainee checkbox for personell type */}
|
{/* Trainee checkbox for personell type */}
|
||||||
{formData.employeeType === 'personell' && (
|
{formData.employeeType === 'personell' && (
|
||||||
<div style={{
|
<div style={{
|
||||||
marginTop: '1rem',
|
marginTop: '1rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
border: '1px solid #e0e0e0',
|
border: '1px solid #e0e0e0',
|
||||||
@@ -692,11 +693,11 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
{hasRole(['admin']) && showContractType && (
|
{hasRole(['admin']) && showContractType && (
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{ margin: '0 0 1rem 0', color: '#0c5460' }}>📝 Vertragstyp</h3>
|
<h3 style={{ margin: '0 0 1rem 0', color: '#0c5460' }}>📝 Vertragstyp</h3>
|
||||||
|
|
||||||
{contractTypeError && (
|
{contractTypeError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginBottom: '1rem',
|
marginBottom: '1rem',
|
||||||
padding: '0.5rem',
|
padding: '0.5rem',
|
||||||
backgroundColor: '#f8d7da',
|
backgroundColor: '#f8d7da',
|
||||||
@@ -706,16 +707,16 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
{contractTypeError}
|
{contractTypeError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
||||||
{contractTypeOptions.map(contract => {
|
{contractTypeOptions.map(contract => {
|
||||||
const isFlexibleDisabled = contract.value === 'flexible' && formData.employeeType === 'personell';
|
const isFlexibleDisabled = contract.value === 'flexible' && formData.employeeType === 'personell';
|
||||||
const isSmallLargeDisabled = (contract.value === 'small' || contract.value === 'large') &&
|
const isSmallLargeDisabled = (contract.value === 'small' || contract.value === 'large') &&
|
||||||
(formData.employeeType === 'manager' || formData.employeeType === 'apprentice');
|
(formData.employeeType === 'manager' || formData.employeeType === 'apprentice');
|
||||||
const isDisabled = isFlexibleDisabled || isSmallLargeDisabled;
|
const isDisabled = isFlexibleDisabled || isSmallLargeDisabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={contract.value}
|
key={contract.value}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -745,8 +746,8 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#2c3e50',
|
color: '#2c3e50',
|
||||||
marginBottom: '4px',
|
marginBottom: '4px',
|
||||||
fontSize: '16px'
|
fontSize: '16px'
|
||||||
@@ -773,8 +774,8 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
color: '#7f8c8d',
|
color: '#7f8c8d',
|
||||||
lineHeight: '1.4'
|
lineHeight: '1.4'
|
||||||
}}>
|
}}>
|
||||||
@@ -801,7 +802,7 @@ const Step2Content: React.FC<StepContentProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step3Content: React.FC<StepContentProps> = ({
|
const Step3Content: React.FC<StepContentProps> = ({
|
||||||
formData,
|
formData,
|
||||||
onInputChange,
|
onInputChange,
|
||||||
onRoleChange,
|
onRoleChange,
|
||||||
@@ -816,11 +817,11 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
{/* Eigenständigkeit */}
|
{/* Eigenständigkeit */}
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{ margin: '0 0 1rem 0', color: '#495057' }}>🎯 Eigenständigkeit</h3>
|
<h3 style={{ margin: '0 0 1rem 0', color: '#495057' }}>🎯 Eigenständigkeit</h3>
|
||||||
|
|
||||||
{canWorkAloneError && (
|
{canWorkAloneError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginBottom: '1rem',
|
marginBottom: '1rem',
|
||||||
padding: '0.5rem',
|
padding: '0.5rem',
|
||||||
backgroundColor: '#f8d7da',
|
backgroundColor: '#f8d7da',
|
||||||
@@ -830,10 +831,10 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
{canWorkAloneError}
|
{canWorkAloneError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '15px',
|
gap: '15px',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
border: '1px solid #e0e0e0',
|
border: '1px solid #e0e0e0',
|
||||||
@@ -847,16 +848,16 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
checked={formData.canWorkAlone}
|
checked={formData.canWorkAlone}
|
||||||
onChange={onInputChange}
|
onChange={onInputChange}
|
||||||
disabled={formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)}
|
disabled={formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)}
|
||||||
style={{
|
style={{
|
||||||
width: '20px',
|
width: '20px',
|
||||||
height: '20px',
|
height: '20px',
|
||||||
opacity: (formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)) ? 0.5 : 1
|
opacity: (formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)) ? 0.5 : 1
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<label htmlFor="canWorkAlone" style={{
|
<label htmlFor="canWorkAlone" style={{
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
color: '#2c3e50',
|
color: '#2c3e50',
|
||||||
display: 'block',
|
display: 'block',
|
||||||
opacity: (formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)) ? 0.5 : 1
|
opacity: (formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)) ? 0.5 : 1
|
||||||
}}>
|
}}>
|
||||||
@@ -864,11 +865,11 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
{(formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)) && ' (Automatisch festgelegt)'}
|
{(formData.employeeType === 'manager' || (formData.employeeType === 'personell' && formData.isTrainee)) && ' (Automatisch festgelegt)'}
|
||||||
</label>
|
</label>
|
||||||
<div style={{ fontSize: '14px', color: '#7f8c8d' }}>
|
<div style={{ fontSize: '14px', color: '#7f8c8d' }}>
|
||||||
{formData.employeeType === 'manager'
|
{formData.employeeType === 'manager'
|
||||||
? 'Chefs sind automatisch als eigenständig markiert.'
|
? 'Chefs sind automatisch als eigenständig markiert.'
|
||||||
: formData.employeeType === 'personell' && formData.isTrainee
|
: formData.employeeType === 'personell' && formData.isTrainee
|
||||||
? 'Auszubildende können nicht als eigenständig markiert werden.'
|
? 'Auszubildende können nicht als eigenständig markiert werden.'
|
||||||
: 'Dieser Mitarbeiter kann komplexe Aufgaben eigenständig lösen und benötigt keine ständige Betreuung.'
|
: 'Dieser Mitarbeiter kann komplexe Aufgaben eigenständig lösen und benötigt keine ständige Betreuung.'
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -890,11 +891,11 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
{hasRole(['admin']) && (
|
{hasRole(['admin']) && (
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{ margin: '0 0 1rem 0', color: '#856404' }}>⚙️ Systemrollen</h3>
|
<h3 style={{ margin: '0 0 1rem 0', color: '#856404' }}>⚙️ Systemrollen</h3>
|
||||||
|
|
||||||
{rolesError && (
|
{rolesError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginBottom: '1rem',
|
marginBottom: '1rem',
|
||||||
padding: '0.5rem',
|
padding: '0.5rem',
|
||||||
backgroundColor: '#f8d7da',
|
backgroundColor: '#f8d7da',
|
||||||
@@ -904,10 +905,10 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
{rolesError}
|
{rolesError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
||||||
{ROLE_CONFIG.map(role => (
|
{ROLE_CONFIG.map(role => (
|
||||||
<div
|
<div
|
||||||
key={role.value}
|
key={role.value}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -951,7 +952,7 @@ const Step3Content: React.FC<StepContentProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Step4Content: React.FC<StepContentProps> = ({
|
const Step4Content: React.FC<StepContentProps> = ({
|
||||||
formData,
|
formData,
|
||||||
passwordForm,
|
passwordForm,
|
||||||
onInputChange,
|
onInputChange,
|
||||||
@@ -970,7 +971,7 @@ const Step4Content: React.FC<StepContentProps> = ({
|
|||||||
{/* Passwort ändern */}
|
{/* Passwort ändern */}
|
||||||
<div>
|
<div>
|
||||||
<h3 style={{ margin: '0 0 1rem 0', color: '#856404' }}>🔒 Passwort zurücksetzen</h3>
|
<h3 style={{ margin: '0 0 1rem 0', color: '#856404' }}>🔒 Passwort zurücksetzen</h3>
|
||||||
|
|
||||||
{!showPasswordSection ? (
|
{!showPasswordSection ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -1009,10 +1010,10 @@ const Step4Content: React.FC<StepContentProps> = ({
|
|||||||
placeholder="Mindestens 6 Zeichen"
|
placeholder="Mindestens 6 Zeichen"
|
||||||
/>
|
/>
|
||||||
{newPasswordError && (
|
{newPasswordError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginTop: '0.25rem'
|
marginTop: '0.25rem'
|
||||||
}}>
|
}}>
|
||||||
{newPasswordError}
|
{newPasswordError}
|
||||||
</div>
|
</div>
|
||||||
@@ -1039,10 +1040,10 @@ const Step4Content: React.FC<StepContentProps> = ({
|
|||||||
placeholder="Passwort wiederholen"
|
placeholder="Passwort wiederholen"
|
||||||
/>
|
/>
|
||||||
{confirmPasswordError && (
|
{confirmPasswordError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginTop: '0.25rem'
|
marginTop: '0.25rem'
|
||||||
}}>
|
}}>
|
||||||
{confirmPasswordError}
|
{confirmPasswordError}
|
||||||
</div>
|
</div>
|
||||||
@@ -1074,9 +1075,9 @@ const Step4Content: React.FC<StepContentProps> = ({
|
|||||||
|
|
||||||
{/* Aktiv Status */}
|
{/* Aktiv Status */}
|
||||||
{mode === 'edit' && (
|
{mode === 'edit' && (
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
border: `1px solid ${isActiveError ? '#dc3545' : '#e0e0e0'}`,
|
border: `1px solid ${isActiveError ? '#dc3545' : '#e0e0e0'}`,
|
||||||
@@ -1099,10 +1100,10 @@ const Step4Content: React.FC<StepContentProps> = ({
|
|||||||
Inaktive Mitarbeiter können sich nicht anmelden und werden nicht für Schichten eingeplant.
|
Inaktive Mitarbeiter können sich nicht anmelden und werden nicht für Schichten eingeplant.
|
||||||
</div>
|
</div>
|
||||||
{isActiveError && (
|
{isActiveError && (
|
||||||
<div style={{
|
<div style={{
|
||||||
color: '#dc3545',
|
color: '#dc3545',
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
marginTop: '0.25rem'
|
marginTop: '0.25rem'
|
||||||
}}>
|
}}>
|
||||||
{isActiveError}
|
{isActiveError}
|
||||||
</div>
|
</div>
|
||||||
@@ -1151,9 +1152,9 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
|
|
||||||
// Inline Step Indicator Komponente (wie in Setup.tsx)
|
// Inline Step Indicator Komponente (wie in Setup.tsx)
|
||||||
const StepIndicator: React.FC = () => (
|
const StepIndicator: React.FC = () => (
|
||||||
<div style={{
|
<div style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: '2.5rem',
|
marginBottom: '2.5rem',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@@ -1169,18 +1170,18 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
backgroundColor: '#e9ecef',
|
backgroundColor: '#e9ecef',
|
||||||
zIndex: 1
|
zIndex: 1
|
||||||
}} />
|
}} />
|
||||||
|
|
||||||
{steps.map((step, index) => {
|
{steps.map((step, index) => {
|
||||||
const isCompleted = index < currentStep;
|
const isCompleted = index < currentStep;
|
||||||
const isCurrent = index === currentStep;
|
const isCurrent = index === currentStep;
|
||||||
const isClickable = index <= currentStep + 1;
|
const isClickable = index <= currentStep + 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.id}
|
key={step.id}
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@@ -1210,18 +1211,18 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
>
|
>
|
||||||
{index + 1}
|
{index + 1}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div style={{ textAlign: 'center' }}>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
fontWeight: isCurrent ? '600' : '400',
|
fontWeight: isCurrent ? '600' : '400',
|
||||||
color: isCurrent ? '#51258f' : '#6c757d'
|
color: isCurrent ? '#51258f' : '#6c757d'
|
||||||
}}>
|
}}>
|
||||||
{step.title}
|
{step.title}
|
||||||
</div>
|
</div>
|
||||||
{step.subtitle && (
|
{step.subtitle && (
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: '12px',
|
fontSize: '12px',
|
||||||
color: '#6c757d',
|
color: '#6c757d',
|
||||||
marginTop: '2px'
|
marginTop: '2px'
|
||||||
}}>
|
}}>
|
||||||
@@ -1275,8 +1276,8 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
showNotification({ // Changed from addNotification to showNotification
|
showNotification({ // Changed from addNotification to showNotification
|
||||||
type: 'success',
|
type: 'success',
|
||||||
title: 'Erfolg',
|
title: 'Erfolg',
|
||||||
message: mode === 'create'
|
message: mode === 'create'
|
||||||
? 'Mitarbeiter wurde erfolgreich erstellt'
|
? 'Mitarbeiter wurde erfolgreich erstellt'
|
||||||
: 'Mitarbeiter wurde erfolgreich aktualisiert'
|
: 'Mitarbeiter wurde erfolgreich aktualisiert'
|
||||||
});
|
});
|
||||||
onSuccess();
|
onSuccess();
|
||||||
@@ -1287,11 +1288,11 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
|
|
||||||
const getNextButtonText = (): string => {
|
const getNextButtonText = (): string => {
|
||||||
if (loading) return '⏳ Wird gespeichert...';
|
if (loading) return '⏳ Wird gespeichert...';
|
||||||
|
|
||||||
if (currentStep === steps.length - 1) {
|
if (currentStep === steps.length - 1) {
|
||||||
return mode === 'create' ? 'Mitarbeiter erstellen' : 'Änderungen speichern';
|
return mode === 'create' ? 'Mitarbeiter erstellen' : 'Änderungen speichern';
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Weiter →';
|
return 'Weiter →';
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1307,8 +1308,8 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
border: '1px solid #e0e0e0',
|
border: '1px solid #e0e0e0',
|
||||||
boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
|
boxShadow: '0 4px 6px rgba(0,0,0,0.1)'
|
||||||
}}>
|
}}>
|
||||||
<h2 style={{
|
<h2 style={{
|
||||||
margin: '0 0 1.5rem 0',
|
margin: '0 0 1.5rem 0',
|
||||||
color: '#2c3e50',
|
color: '#2c3e50',
|
||||||
borderBottom: '2px solid #f0f0f0',
|
borderBottom: '2px solid #f0f0f0',
|
||||||
paddingBottom: '1rem',
|
paddingBottom: '1rem',
|
||||||
@@ -1322,16 +1323,16 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
|
|
||||||
{/* Aktueller Schritt Titel und Beschreibung */}
|
{/* Aktueller Schritt Titel und Beschreibung */}
|
||||||
<div style={{ textAlign: 'center', marginBottom: '1.5rem' }}>
|
<div style={{ textAlign: 'center', marginBottom: '1.5rem' }}>
|
||||||
<h3 style={{
|
<h3 style={{
|
||||||
fontSize: '1.25rem',
|
fontSize: '1.25rem',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginBottom: '0.5rem',
|
marginBottom: '0.5rem',
|
||||||
color: '#2c3e50'
|
color: '#2c3e50'
|
||||||
}}>
|
}}>
|
||||||
{steps[currentStep].title}
|
{steps[currentStep].title}
|
||||||
</h3>
|
</h3>
|
||||||
{steps[currentStep].subtitle && (
|
{steps[currentStep].subtitle && (
|
||||||
<p style={{
|
<p style={{
|
||||||
color: '#6c757d',
|
color: '#6c757d',
|
||||||
fontSize: '1rem'
|
fontSize: '1rem'
|
||||||
}}>
|
}}>
|
||||||
@@ -1346,9 +1347,9 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Navigations-Buttons */}
|
{/* Navigations-Buttons */}
|
||||||
<div style={{
|
<div style={{
|
||||||
marginTop: '2rem',
|
marginTop: '2rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}>
|
}}>
|
||||||
@@ -1368,7 +1369,7 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
>
|
>
|
||||||
{currentStep === 0 ? 'Abbrechen' : '← Zurück'}
|
{currentStep === 0 ? 'Abbrechen' : '← Zurück'}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={isLastStep ? handleFinalSubmit : goToNextStep}
|
onClick={isLastStep ? handleFinalSubmit : goToNextStep}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
@@ -1390,16 +1391,16 @@ const EmployeeForm: React.FC<EmployeeFormProps> = ({
|
|||||||
|
|
||||||
{/* Zusätzliche Informationen */}
|
{/* Zusätzliche Informationen */}
|
||||||
{isLastStep && !loading && (
|
{isLastStep && !loading && (
|
||||||
<div style={{
|
<div style={{
|
||||||
marginTop: '1.5rem',
|
marginTop: '1.5rem',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: '#6c757d',
|
color: '#6c757d',
|
||||||
fontSize: '0.9rem',
|
fontSize: '0.9rem',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
backgroundColor: '#f8f9fa',
|
backgroundColor: '#f8f9fa',
|
||||||
borderRadius: '6px'
|
borderRadius: '6px'
|
||||||
}}>
|
}}>
|
||||||
{mode === 'create'
|
{mode === 'create'
|
||||||
? 'Überprüfen Sie alle Daten, bevor Sie den Mitarbeiter erstellen'
|
? 'Überprüfen Sie alle Daten, bevor Sie den Mitarbeiter erstellen'
|
||||||
: 'Überprüfen Sie alle Änderungen, bevor Sie sie speichern'
|
: 'Überprüfen Sie alle Änderungen, bevor Sie sie speichern'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,16 +14,16 @@ export default defineConfig(({ mode }) => {
|
|||||||
NODE_ENV: mode,
|
NODE_ENV: mode,
|
||||||
ENABLE_PRO: env.ENABLE_PRO || 'false',
|
ENABLE_PRO: env.ENABLE_PRO || 'false',
|
||||||
VITE_APP_TITLE: env.APP_TITLE || 'Shift Planning App',
|
VITE_APP_TITLE: env.APP_TITLE || 'Shift Planning App',
|
||||||
VITE_API_URL: isProduction ? '/api' : '/api',
|
VITE_API_URL: isProduction ? '/api' : 'http://localhost:3002/api',
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
|
|
||||||
server: {
|
server: isDevelopment ? undefined : {
|
||||||
port: 3003,
|
port: 3002,
|
||||||
host: true,
|
host: true,
|
||||||
//open: isDevelopment,
|
open: true,
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:3002',
|
target: 'http://localhost:3002',
|
||||||
|
|||||||
161
package-lock.json
generated
161
package-lock.json
generated
@@ -35,6 +35,7 @@
|
|||||||
"@types/jest": "^29.5.0",
|
"@types/jest": "^29.5.0",
|
||||||
"@types/jsonwebtoken": "^9.0.2",
|
"@types/jsonwebtoken": "^9.0.2",
|
||||||
"@types/uuid": "^9.0.2",
|
"@types/uuid": "^9.0.2",
|
||||||
|
"cross-env": "10.1.0",
|
||||||
"ts-node": "^10.9.0",
|
"ts-node": "^10.9.0",
|
||||||
"tsx": "^4.0.0",
|
"tsx": "^4.0.0",
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
@@ -259,6 +260,7 @@
|
|||||||
"backend/node_modules/@types/node": {
|
"backend/node_modules/@types/node": {
|
||||||
"version": "24.7.0",
|
"version": "24.7.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~7.14.0"
|
"undici-types": "~7.14.0"
|
||||||
}
|
}
|
||||||
@@ -890,11 +892,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"backend/node_modules/isexe": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"license": "ISC",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"backend/node_modules/jest-diff": {
|
"backend/node_modules/jest-diff": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -1777,20 +1774,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"backend/node_modules/which": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"license": "ISC",
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"isexe": "^2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"node-which": "bin/node-which"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"backend/node_modules/wide-align": {
|
"backend/node_modules/wide-align": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
@@ -1874,6 +1857,7 @@
|
|||||||
"version": "7.28.5",
|
"version": "7.28.5",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.27.1",
|
"@babel/code-frame": "^7.27.1",
|
||||||
"@babel/generator": "^7.28.5",
|
"@babel/generator": "^7.28.5",
|
||||||
@@ -2116,6 +2100,13 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@epic-web/invariant": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.21.5",
|
"version": "0.21.5",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
@@ -2297,10 +2288,6 @@
|
|||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@schichtenplaner/premium": {
|
|
||||||
"resolved": "premium",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/@sinclair/typebox": {
|
"node_modules/@sinclair/typebox": {
|
||||||
"version": "0.34.41",
|
"version": "0.34.41",
|
||||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz",
|
||||||
@@ -2321,7 +2308,6 @@
|
|||||||
"integrity": "sha512-hcFZIjW8yQz8O8//2WTIXylm5Xsgc+lW9ISLgUk1xGmptIJQRdlhVIXCpSyLrQaaRiyhQRaVg7l3BD9S216BHw==",
|
"integrity": "sha512-hcFZIjW8yQz8O8//2WTIXylm5Xsgc+lW9ISLgUk1xGmptIJQRdlhVIXCpSyLrQaaRiyhQRaVg7l3BD9S216BHw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
},
|
},
|
||||||
@@ -2565,7 +2551,6 @@
|
|||||||
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
|
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/deep-eql": "*",
|
"@types/deep-eql": "*",
|
||||||
"assertion-error": "^2.0.1"
|
"assertion-error": "^2.0.1"
|
||||||
@@ -2576,8 +2561,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
||||||
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
|
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
@@ -2639,6 +2623,7 @@
|
|||||||
"version": "19.2.2",
|
"version": "19.2.2",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
@@ -2719,7 +2704,6 @@
|
|||||||
"integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
|
"integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/chai": "^5.2.2",
|
"@types/chai": "^5.2.2",
|
||||||
"@vitest/spy": "3.2.4",
|
"@vitest/spy": "3.2.4",
|
||||||
@@ -2737,7 +2721,6 @@
|
|||||||
"integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
|
"integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/spy": "3.2.4",
|
"@vitest/spy": "3.2.4",
|
||||||
"estree-walker": "^3.0.3",
|
"estree-walker": "^3.0.3",
|
||||||
@@ -2765,7 +2748,6 @@
|
|||||||
"integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
|
"integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tinyrainbow": "^2.0.0"
|
"tinyrainbow": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -2779,7 +2761,6 @@
|
|||||||
"integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
|
"integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tinyspy": "^4.0.3"
|
"tinyspy": "^4.0.3"
|
||||||
},
|
},
|
||||||
@@ -2793,7 +2774,6 @@
|
|||||||
"integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
|
"integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vitest/pretty-format": "3.2.4",
|
"@vitest/pretty-format": "3.2.4",
|
||||||
"loupe": "^3.1.4",
|
"loupe": "^3.1.4",
|
||||||
@@ -2877,7 +2857,6 @@
|
|||||||
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
|
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
@@ -2888,7 +2867,6 @@
|
|||||||
"integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==",
|
"integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.0.1"
|
"tslib": "^2.0.1"
|
||||||
},
|
},
|
||||||
@@ -2981,6 +2959,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"baseline-browser-mapping": "^2.8.19",
|
"baseline-browser-mapping": "^2.8.19",
|
||||||
"caniuse-lite": "^1.0.30001751",
|
"caniuse-lite": "^1.0.30001751",
|
||||||
@@ -3065,7 +3044,6 @@
|
|||||||
"integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
|
"integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assertion-error": "^2.0.1",
|
"assertion-error": "^2.0.1",
|
||||||
"check-error": "^2.1.1",
|
"check-error": "^2.1.1",
|
||||||
@@ -3100,7 +3078,6 @@
|
|||||||
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
|
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16"
|
"node": ">= 16"
|
||||||
}
|
}
|
||||||
@@ -3201,6 +3178,39 @@
|
|||||||
"url": "https://opencollective.com/core-js"
|
"url": "https://opencollective.com/core-js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-env": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@epic-web/invariant": "^1.0.0",
|
||||||
|
"cross-spawn": "^7.0.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"cross-env": "dist/bin/cross-env.js",
|
||||||
|
"cross-env-shell": "dist/bin/cross-env-shell.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cross-spawn": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.1.0",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"which": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/css.escape": {
|
"node_modules/css.escape": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
|
||||||
@@ -3245,7 +3255,6 @@
|
|||||||
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
|
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
@@ -3407,7 +3416,6 @@
|
|||||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"esparse": "bin/esparse.js",
|
"esparse": "bin/esparse.js",
|
||||||
"esvalidate": "bin/esvalidate.js"
|
"esvalidate": "bin/esvalidate.js"
|
||||||
@@ -3422,7 +3430,6 @@
|
|||||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0"
|
"@types/estree": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -3846,6 +3853,13 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isexe": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/jest-diff": {
|
"node_modules/jest-diff": {
|
||||||
"version": "30.2.0",
|
"version": "30.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz",
|
||||||
@@ -3980,8 +3994,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
|
||||||
"integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
|
"integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
@@ -4007,7 +4020,6 @@
|
|||||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
}
|
}
|
||||||
@@ -4203,6 +4215,16 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-key": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "0.1.12",
|
"version": "0.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||||
@@ -4215,7 +4237,6 @@
|
|||||||
"integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
|
"integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14.16"
|
"node": ">= 14.16"
|
||||||
}
|
}
|
||||||
@@ -4346,6 +4367,7 @@
|
|||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "19.2.0",
|
"version": "19.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -4353,6 +4375,7 @@
|
|||||||
"node_modules/react-dom": {
|
"node_modules/react-dom": {
|
||||||
"version": "19.2.0",
|
"version": "19.2.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"scheduler": "^0.27.0"
|
"scheduler": "^0.27.0"
|
||||||
},
|
},
|
||||||
@@ -4409,7 +4432,6 @@
|
|||||||
"integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==",
|
"integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ast-types": "^0.16.1",
|
"ast-types": "^0.16.1",
|
||||||
"esprima": "~4.0.0",
|
"esprima": "~4.0.0",
|
||||||
@@ -4586,6 +4608,29 @@
|
|||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/shebang-command": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"shebang-regex": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||||
@@ -4725,7 +4770,6 @@
|
|||||||
"integrity": "sha512-WQMfNsbyHO8fx5bLatsLATzvFwzG42xHnq8W9s2HzjhGzQ2Pp77k7nRWXMuRNvvv+4ypKhJdqeDl8S9kwWuz2A==",
|
"integrity": "sha512-WQMfNsbyHO8fx5bLatsLATzvFwzG42xHnq8W9s2HzjhGzQ2Pp77k7nRWXMuRNvvv+4ypKhJdqeDl8S9kwWuz2A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@storybook/global": "^5.0.0",
|
"@storybook/global": "^5.0.0",
|
||||||
"@storybook/icons": "^1.6.0",
|
"@storybook/icons": "^1.6.0",
|
||||||
@@ -4761,7 +4805,6 @@
|
|||||||
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"peer": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
},
|
},
|
||||||
@@ -4801,6 +4844,7 @@
|
|||||||
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
|
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
"acorn": "^8.15.0",
|
"acorn": "^8.15.0",
|
||||||
@@ -4819,8 +4863,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.15",
|
"version": "0.2.15",
|
||||||
@@ -4843,7 +4886,6 @@
|
|||||||
"integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
|
"integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
@@ -4854,7 +4896,6 @@
|
|||||||
"integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
|
"integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
@@ -4905,6 +4946,7 @@
|
|||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
"peer": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
@@ -4987,6 +5029,7 @@
|
|||||||
"version": "6.4.1",
|
"version": "6.4.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
"fdir": "^6.4.4",
|
"fdir": "^6.4.4",
|
||||||
@@ -5111,13 +5154,28 @@
|
|||||||
"@esbuild/win32-x64": "0.25.11"
|
"@esbuild/win32-x64": "0.25.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
|
"devOptional": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-which": "bin/node-which"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.18.3",
|
"version": "8.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
@@ -5142,6 +5200,7 @@
|
|||||||
"premium": {
|
"premium": {
|
||||||
"name": "@schichtenplaner/premium",
|
"name": "@schichtenplaner/premium",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"extraneous": true,
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"backendPRO",
|
"backendPRO",
|
||||||
"frontendPRO"
|
"frontendPRO"
|
||||||
|
|||||||
Reference in New Issue
Block a user