Führerschein und Sicherheitsüberprüfung Ja-Nein

Dieser Commit ist enthalten in:
Claude Project Manager
2025-09-29 20:15:36 +02:00
Ursprung e821fb0567
Commit 68a9a50227
5 geänderte Dateien mit 132 neuen und 36 gelöschten Zeilen

Datei anzeigen

@ -5,9 +5,9 @@
## Project Overview
- **Path**: `A:\GiTea\SkillMate`
- **Files**: 189 files
- **Size**: 4.6 MB
- **Last Modified**: 2025-09-29 01:39
- **Files**: 268 files
- **Size**: 5.7 MB
- **Last Modified**: 2025-09-29 20:12
## Technology Stack
@ -84,6 +84,59 @@ backend/
│ ├── skillmate.dev.encrypted.db
│ ├── skillmate.dev.encrypted.db-shm
│ ├── skillmate.dev.encrypted.db-wal
│ ├── dist/
│ │ ├── index.js
│ │ ├── index.js.map
│ │ ├── config/
│ │ │ ├── appConfig.js
│ │ │ ├── appConfig.js.map
│ │ │ ├── database.js
│ │ │ ├── database.js.map
│ │ │ ├── secureDatabase.js
│ │ │ └── secureDatabase.js.map
│ │ ├── middleware/
│ │ │ ├── auth.js
│ │ │ ├── auth.js.map
│ │ │ ├── errorHandler.js
│ │ │ ├── errorHandler.js.map
│ │ │ ├── roleAuth.js
│ │ │ └── roleAuth.js.map
│ │ ├── repositories/
│ │ │ ├── employeeRepository.js
│ │ │ └── employeeRepository.js.map
│ │ ├── routes/
│ │ │ ├── analytics.js
│ │ │ ├── analytics.js.map
│ │ │ ├── auth.js
│ │ │ ├── auth.js.map
│ │ │ ├── employeeOrganization.js
│ │ │ ├── employeeOrganization.js.map
│ │ │ ├── employees.js
│ │ │ ├── employees.js.map
│ │ │ ├── employeesSecure.js
│ │ │ └── employeesSecure.js.map
│ │ ├── services/
│ │ │ ├── auditService.js
│ │ │ ├── auditService.js.map
│ │ │ ├── emailService.js
│ │ │ ├── emailService.js.map
│ │ │ ├── encryption.js
│ │ │ ├── encryption.js.map
│ │ │ ├── reminderService.js
│ │ │ ├── reminderService.js.map
│ │ │ ├── skillSeeder.js
│ │ │ └── skillSeeder.js.map
│ │ ├── usecases/
│ │ │ ├── employees.js
│ │ │ ├── employees.js.map
│ │ │ ├── users.js
│ │ │ └── users.js.map
│ │ ├── utils/
│ │ │ ├── logger.js
│ │ │ └── logger.js.map
│ │ └── validation/
│ │ ├── employeeValidators.js
│ │ └── employeeValidators.js.map
│ ├── logs/
│ │ ├── combined.log
│ │ └── error.log
@ -194,6 +247,8 @@ frontend/
│ │ └── skills.ts
│ ├── types/
│ │ └── electron.d.ts
│ ├── utils/
│ │ └── skillRules.ts
│ └── views/
│ ├── Dashboard.tsx
│ ├── DeskBooking.tsx
@ -249,3 +304,4 @@ This project is managed with Claude Project Manager. To work with this project:
- README updated on 2025-09-27 12:01:06
- README updated on 2025-09-28 18:39:07
- README updated on 2025-09-29 19:21:55
- README updated on 2025-09-29 20:15:07

Datei anzeigen

@ -0,0 +1,7 @@
export const BOOLEAN_SKILL_GROUPS = new Set([
'certifications.vehicles',
'certifications.security_clearance'
])
export const isBooleanSkill = (categoryId: string, subCategoryId: string) =>
BOOLEAN_SKILL_GROUPS.has(`${categoryId}.${subCategoryId}`)

Datei anzeigen

@ -5,7 +5,8 @@ import type { Employee } from '@skillmate/shared'
import SkillLevelBar from '../components/SkillLevelBar'
import OfficeMapModal from '../components/OfficeMapModal'
import { employeeApi } from '../services/api'
import { useAuthStore } from '../stores/authStore'
import { useAuthStore } from '../stores/authStore'
import { isBooleanSkill } from '../utils/skillRules'
export default function EmployeeDetail() {
const { id } = useParams()
@ -210,12 +211,18 @@ export default function EmployeeDetail() {
{selected.map((sk) => {
const info = employee.skills.find(es => es.id === sk.id)
const levelVal = info?.level ? Number(info.level) : ''
const booleanSkill = isBooleanSkill(cat.id, sub.id)
return (
<li key={sk.id}>
<div className="flex items-center justify-between mb-1">
<span className="text-secondary">{sk.name}</span>
{booleanSkill && info && (
<span className="text-small font-medium text-green-700 dark:text-green-400">Ja</span>
)}
</div>
<SkillLevelBar value={levelVal as any} onChange={() => {}} disabled showHelp={false} />
{!booleanSkill && (
<SkillLevelBar value={levelVal as any} onChange={() => {}} disabled showHelp={false} />
)}
</li>
)
})}

Datei anzeigen

@ -5,6 +5,7 @@ import { employeeApi } from '../services/api'
import { SKILL_HIERARCHY, LANGUAGE_LEVELS } from '../data/skillCategories'
import PhotoPreview from '../components/PhotoPreview'
import OrganizationSelector from '../components/OrganizationSelector'
import { isBooleanSkill } from '../utils/skillRules'
export default function EmployeeForm() {
const navigate = useNavigate()
@ -87,7 +88,7 @@ export default function EmployeeForm() {
subCategoryId,
skillId,
name: skillName,
level: ''
level: isBooleanSkill(categoryId, subCategoryId) ? 'yes' : ''
})
}
return { ...prev, skills }
@ -95,6 +96,7 @@ export default function EmployeeForm() {
}
const handleSkillLevelChange = (categoryId: string, subCategoryId: string, skillId: string, level: string) => {
if (isBooleanSkill(categoryId, subCategoryId)) return
setFormData(prev => {
const skills = [...prev.skills]
const skill = skills.find(s =>
@ -204,10 +206,10 @@ export default function EmployeeForm() {
const newEmployee: Partial<Employee> = {
...formData,
skills: formData.skills.map((skill, index) => ({
id: `skill-${index}`,
id: skill.skillId || `skill-${index}`,
name: skill.name,
category: skill.categoryId,
level: skill.level || 3
level: isBooleanSkill(skill.categoryId, skill.subCategoryId) ? null : (skill.level || 'Basic')
})),
languages: formData.skills
.filter(s => s.subCategoryId === 'languages')
@ -659,7 +661,9 @@ export default function EmployeeForm() {
{/* Niveauauswahl */}
{isSkillSelected(category.id, subCategory.id, skill.id) && (
subCategory.id === 'languages' ? (
isBooleanSkill(category.id, subCategory.id) ? (
<span className="text-sm font-medium text-green-700 dark:text-green-400">Ja</span>
) : subCategory.id === 'languages' ? (
<select
value={getSkillLevel(category.id, subCategory.id, skill.id)}
onChange={(e) => handleSkillLevelChange(category.id, subCategory.id, skill.id, e.target.value)}

Datei anzeigen

@ -5,6 +5,7 @@ import PhotoUpload from '../components/PhotoUpload'
import SkillLevelBar from '../components/SkillLevelBar'
import DeputyManagement from '../components/DeputyManagement'
import OrganizationSelector from '../components/OrganizationSelector'
import { isBooleanSkill } from '../utils/skillRules'
interface SkillSelection { categoryId: string; subCategoryId: string; skillId: string; name: string; level: string }
@ -53,12 +54,16 @@ const AVAILABILITY_OPTIONS = [
const mapped: SkillSelection[] = (data.skills || []).map((s: any) => {
const catStr = s.category || ''
const [catId, subId] = String(catStr).split('.')
const categoryId = catId || ''
const subCategoryId = subId || ''
const booleanSkill = isBooleanSkill(categoryId, subCategoryId)
const rawLevel = (s.level ?? '').toString()
return {
categoryId: catId || '',
subCategoryId: subId || '',
categoryId,
subCategoryId,
skillId: s.id,
name: s.name,
level: (s.level || '').toString()
level: booleanSkill ? (rawLevel && rawLevel !== '0' ? '1' : '') : rawLevel
}
})
setSkills(mapped)
@ -102,13 +107,14 @@ const AVAILABILITY_OPTIONS = [
if (idx >= 0) {
list.splice(idx, 1)
} else {
list.push({ categoryId, subCategoryId, skillId, name: skillName, level: '' })
list.push({ categoryId, subCategoryId, skillId, name: skillName, level: isBooleanSkill(categoryId, subCategoryId) ? '1' : '' })
}
return list
})
}
const handleSkillLevelChange = (categoryId: string, subCategoryId: string, skillId: string, level: string) => {
if (isBooleanSkill(categoryId, subCategoryId)) return
setSkills(prev => prev.map(s => (s.categoryId === categoryId && s.subCategoryId === subCategoryId && s.skillId === skillId) ? { ...s, level } : s))
}
@ -162,7 +168,12 @@ const AVAILABILITY_OPTIONS = [
mobile: form.mobile || null,
office: form.office || null,
availability: form.availability || 'available',
skills: skills.map((s, i) => ({ id: s.skillId, name: s.name, category: s.categoryId, level: Number(s.level) || 3 })),
skills: skills.map(s => ({
id: s.skillId,
name: s.name,
category: s.categoryId,
level: isBooleanSkill(s.categoryId, s.subCategoryId) ? null : (Number(s.level) || 3)
})),
languages: form.languages || [],
specializations: form.specializations || []
}
@ -327,29 +338,40 @@ const AVAILABILITY_OPTIONS = [
<div key={`${category.id}-${sub.id}`} className="mb-3">
<p className="text-small text-tertiary mb-2">{sub.name}</p>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
{sub.skills.map(skill => (
<div key={`${category.id}-${sub.id}-${skill.id}`} className={`p-2 border rounded-input ${isSkillSelected(category.id, sub.id, skill.id) ? 'border-primary-blue bg-bg-accent' : 'border-border-default'}`}>
<label className="flex items-center justify-between">
<span className="text-body text-secondary">
<input
type="checkbox"
className="mr-2"
checked={isSkillSelected(category.id, sub.id, skill.id)}
onChange={() => handleSkillToggle(category.id, sub.id, skill.id, skill.name)}
/>
{skill.name}
</span>
{isSkillSelected(category.id, sub.id, skill.id) && (
<div className="ml-3 flex-1">
<SkillLevelBar
value={Number(getSkillLevel(category.id, sub.id, skill.id)) || ''}
onChange={(val) => handleSkillLevelChange(category.id, sub.id, skill.id, String(val))}
{sub.skills.map(skill => {
const booleanSkill = isBooleanSkill(category.id, sub.id)
const selected = isSkillSelected(category.id, sub.id, skill.id)
return (
<div
key={`${category.id}-${sub.id}-${skill.id}`}
className={`p-2 border rounded-input ${selected ? 'border-primary-blue bg-bg-accent' : 'border-border-default'}`}
>
<label className="flex items-center justify-between">
<span className="text-body text-secondary">
<input
type="checkbox"
className="mr-2"
checked={selected}
onChange={() => handleSkillToggle(category.id, sub.id, skill.id, skill.name)}
/>
</div>
)}
</label>
</div>
))}
{skill.name}
</span>
{selected && (
booleanSkill ? (
<span className="ml-3 text-small font-medium text-green-700 dark:text-green-400">Ja</span>
) : (
<div className="ml-3 flex-1">
<SkillLevelBar
value={Number(getSkillLevel(category.id, sub.id, skill.id)) || ''}
onChange={(val) => handleSkillLevelChange(category.id, sub.id, skill.id, String(val))}
/>
</div>
)
)}
</label>
</div>
)
})}
</div>
</div>
))}