2a48fb7af0
- DB migration v32: task_assignments and event_assignments join tables with CASCADE delete; existing assigned_to data migrated automatically - Tasks API: accepts assigned_to as array, returns assigned_users[] with json_group_array; filter uses EXISTS on task_assignments - Calendar API: same pattern via event_assignments; serializeEvent includes assigned_users array - Recurring task completion copies all assignments to the new instance - Frontend: shared UserMultiSelect component with avatar stack display (renderAvatarStack, renderUserMultiSelect, getSelectedUserIds, bindUserMultiSelect); tasks.js and calendar.js use it in modals and card/agenda views - CSS: user-multi-select.css with avatar-stack and user-ms classes - 14 new tests covering CRUD, JSON aggregation, EXISTS filter, and CASCADE behavior for both task and event assignments Closes #125
103 lines
2.3 KiB
CSS
103 lines
2.3 KiB
CSS
/* ------------------------------------------------------------------ */
|
|
/* Avatar-Stack (Mehrfach-Zuweisung Anzeige) */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
.avatar-stack {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
flex-direction: row-reverse;
|
|
}
|
|
|
|
.avatar-stack__item {
|
|
border-radius: var(--radius-full);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-on-accent);
|
|
border: 2px solid var(--color-surface);
|
|
flex-shrink: 0;
|
|
margin-left: -6px;
|
|
}
|
|
|
|
.avatar-stack__item:last-child {
|
|
margin-left: 0;
|
|
}
|
|
|
|
.avatar-stack__overflow {
|
|
background-color: var(--color-text-secondary);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* User Multi-Select Widget */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
.user-ms {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-1);
|
|
}
|
|
|
|
.user-ms__options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-1);
|
|
max-height: 180px;
|
|
overflow-y: auto;
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
padding: var(--space-1);
|
|
background: var(--color-surface);
|
|
}
|
|
|
|
.user-ms__option {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-2);
|
|
padding: var(--space-1) var(--space-2);
|
|
border-radius: var(--radius-sm);
|
|
cursor: pointer;
|
|
transition: background var(--transition-fast);
|
|
user-select: none;
|
|
}
|
|
|
|
.user-ms__option:hover {
|
|
background: var(--color-surface-alt);
|
|
}
|
|
|
|
.user-ms__checkbox {
|
|
accent-color: var(--color-accent);
|
|
width: 16px;
|
|
height: 16px;
|
|
flex-shrink: 0;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.user-ms__avatar {
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: var(--radius-full);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: var(--text-xs);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-text-on-accent);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.user-ms__avatar--none {
|
|
background-color: var(--color-border);
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
.user-ms__name {
|
|
font-size: var(--text-sm);
|
|
color: var(--color-text-primary);
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|