diff --git a/apps/todo/apps/web/src/lib/components/TaskEditModal.svelte b/apps/todo/apps/web/src/lib/components/TaskEditModal.svelte
index 2ed79e38b..b41d6999a 100644
--- a/apps/todo/apps/web/src/lib/components/TaskEditModal.svelte
+++ b/apps/todo/apps/web/src/lib/components/TaskEditModal.svelte
@@ -1,16 +1,15 @@
@@ -304,20 +213,7 @@
@@ -346,68 +242,10 @@
@@ -441,140 +279,22 @@
@@ -788,184 +508,6 @@
min-height: 80px;
}
- /* Priority buttons */
- .priority-buttons {
- display: flex;
- gap: 0.5rem;
- flex-wrap: wrap;
- }
-
- .priority-btn {
- display: flex;
- align-items: center;
- gap: 0.375rem;
- padding: 0.5rem 0.875rem;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 9999px;
- background: rgba(255, 255, 255, 0.8);
- font-size: 0.8125rem;
- color: #374151;
- cursor: pointer;
- transition: all 0.15s;
- }
-
- :global(.dark) .priority-btn {
- background: rgba(255, 255, 255, 0.1);
- border-color: rgba(255, 255, 255, 0.15);
- color: #e5e7eb;
- }
-
- .priority-btn:hover {
- border-color: var(--priority-color);
- }
-
- .priority-btn.selected {
- background: color-mix(in srgb, var(--priority-color) 15%, transparent);
- border-color: var(--priority-color);
- color: var(--priority-color);
- }
-
- .priority-dot {
- width: 0.5rem;
- height: 0.5rem;
- border-radius: 9999px;
- }
-
- /* Label selector */
- .label-selector {
- position: relative;
- }
-
- .label-trigger {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0.625rem 0.875rem;
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 0.75rem;
- background: rgba(255, 255, 255, 0.8);
- cursor: pointer;
- transition: all 0.15s;
- }
-
- :global(.dark) .label-trigger {
- background: rgba(255, 255, 255, 0.1);
- border-color: rgba(255, 255, 255, 0.15);
- }
-
- .label-trigger:hover {
- border-color: rgba(0, 0, 0, 0.25);
- }
-
- .text-muted {
- color: #9ca3af;
- font-size: 0.875rem;
- }
-
- .selected-labels {
- display: flex;
- flex-wrap: wrap;
- gap: 0.375rem;
- }
-
- .label-tag {
- font-size: 0.75rem;
- padding: 0.125rem 0.5rem;
- border-radius: 9999px;
- background: color-mix(in srgb, var(--label-color) 15%, transparent);
- color: var(--label-color);
- font-weight: 500;
- }
-
- .label-more {
- font-size: 0.75rem;
- color: #6b7280;
- }
-
- .dropdown-arrow {
- width: 1rem;
- height: 1rem;
- color: #9ca3af;
- }
-
- .label-dropdown {
- position: absolute;
- top: calc(100% + 0.5rem);
- left: 0;
- right: 0;
- max-height: 200px;
- overflow-y: auto;
- padding: 0.375rem;
- border-radius: 0.75rem;
- background: rgba(255, 255, 255, 0.95);
- backdrop-filter: blur(12px);
- border: 1px solid rgba(0, 0, 0, 0.1);
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
- z-index: 10;
- }
-
- :global(.dark) .label-dropdown {
- background: rgba(40, 40, 40, 0.95);
- border-color: rgba(255, 255, 255, 0.15);
- }
-
- .label-option {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- width: 100%;
- padding: 0.5rem 0.75rem;
- border: none;
- background: transparent;
- border-radius: 0.5rem;
- cursor: pointer;
- transition: background 0.15s;
- text-align: left;
- }
-
- .label-option:hover {
- background: rgba(0, 0, 0, 0.05);
- }
-
- :global(.dark) .label-option:hover {
- background: rgba(255, 255, 255, 0.1);
- }
-
- .label-option.selected {
- background: rgba(139, 92, 246, 0.1);
- }
-
- .label-dot {
- width: 0.625rem;
- height: 0.625rem;
- border-radius: 9999px;
- flex-shrink: 0;
- }
-
- .label-name {
- flex: 1;
- font-size: 0.875rem;
- color: #374151;
- }
-
- :global(.dark) .label-name {
- color: #e5e7eb;
- }
-
- .check-icon {
- width: 1rem;
- height: 1rem;
- color: #8b5cf6;
- }
-
- .no-labels {
- padding: 0.75rem;
- text-align: center;
- font-size: 0.875rem;
- color: #9ca3af;
- }
-
/* Footer */
.modal-footer {
display: flex;
@@ -1056,164 +598,6 @@
}
}
- /* Storypoints */
- .storypoint-buttons {
- display: flex;
- gap: 0.375rem;
- flex-wrap: wrap;
- align-items: center;
- }
-
- .storypoint-btn {
- min-width: 2.25rem;
- height: 2.25rem;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0 0.5rem;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 9999px;
- background: rgba(255, 255, 255, 0.8);
- font-size: 0.8125rem;
- font-weight: 500;
- color: #374151;
- cursor: pointer;
- transition: all 0.15s;
- }
-
- :global(.dark) .storypoint-btn {
- background: rgba(255, 255, 255, 0.1);
- border-color: rgba(255, 255, 255, 0.15);
- color: #e5e7eb;
- }
-
- .storypoint-btn:hover {
- border-color: #8b5cf6;
- }
-
- .storypoint-btn.selected {
- background: rgba(139, 92, 246, 0.15);
- border-color: #8b5cf6;
- color: #8b5cf6;
- }
-
- .storypoint-clear,
- .duration-clear,
- .fun-rating-clear {
- width: 2rem;
- height: 2rem;
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0;
- border: none;
- border-radius: 9999px;
- background: rgba(239, 68, 68, 0.1);
- color: #ef4444;
- cursor: pointer;
- transition: all 0.15s;
- }
-
- .storypoint-clear:hover,
- .duration-clear:hover,
- .fun-rating-clear:hover {
- background: rgba(239, 68, 68, 0.2);
- }
-
- /* Duration */
- .duration-buttons {
- display: flex;
- gap: 0.375rem;
- flex-wrap: wrap;
- align-items: center;
- }
-
- .duration-btn {
- padding: 0.5rem 0.75rem;
- border: 1px solid rgba(0, 0, 0, 0.1);
- border-radius: 9999px;
- background: rgba(255, 255, 255, 0.8);
- font-size: 0.8125rem;
- color: #374151;
- cursor: pointer;
- transition: all 0.15s;
- }
-
- :global(.dark) .duration-btn {
- background: rgba(255, 255, 255, 0.1);
- border-color: rgba(255, 255, 255, 0.15);
- color: #e5e7eb;
- }
-
- .duration-btn:hover {
- border-color: #8b5cf6;
- }
-
- .duration-btn.selected {
- background: rgba(139, 92, 246, 0.15);
- border-color: #8b5cf6;
- color: #8b5cf6;
- }
-
- .duration-custom {
- display: flex;
- gap: 0.5rem;
- margin-top: 0.75rem;
- }
-
- .duration-input {
- width: 80px;
- }
-
- .duration-unit {
- width: 120px;
- }
-
- /* Fun Rating */
- .fun-rating {
- display: flex;
- gap: 0.25rem;
- align-items: center;
- }
-
- .fun-rating-dot {
- padding: 0.25rem;
- border: none;
- background: transparent;
- cursor: pointer;
- transition: transform 0.15s;
- }
-
- .fun-rating-dot:hover {
- transform: scale(1.2);
- }
-
- .fun-rating-dot .dot {
- display: block;
- width: 1.25rem;
- height: 1.25rem;
- border-radius: 9999px;
- background: rgba(0, 0, 0, 0.1);
- transition: all 0.15s;
- }
-
- :global(.dark) .fun-rating-dot .dot {
- background: rgba(255, 255, 255, 0.15);
- }
-
- .fun-rating-dot.filled .dot {
- background: var(--dot-color);
- }
-
- .fun-rating-labels {
- display: flex;
- justify-content: space-between;
- padding: 0 0.25rem;
- margin-top: 0.25rem;
- font-size: 0.6875rem;
- color: #9ca3af;
- }
-
.fun-rating-value {
font-weight: 600;
}
diff --git a/apps/todo/apps/web/src/lib/components/form/DurationPicker.svelte b/apps/todo/apps/web/src/lib/components/form/DurationPicker.svelte
new file mode 100644
index 000000000..c0be0f92a
--- /dev/null
+++ b/apps/todo/apps/web/src/lib/components/form/DurationPicker.svelte
@@ -0,0 +1,238 @@
+
+
+
+
+
+ {#if showCustom}
+
+
+
+ {#each unitOptions as unit}
+ {unit.label}
+ {/each}
+
+
+ {/if}
+
+
+
diff --git a/apps/todo/apps/web/src/lib/components/form/FunRatingPicker.svelte b/apps/todo/apps/web/src/lib/components/form/FunRatingPicker.svelte
new file mode 100644
index 000000000..2d2fbbbd6
--- /dev/null
+++ b/apps/todo/apps/web/src/lib/components/form/FunRatingPicker.svelte
@@ -0,0 +1,127 @@
+
+
+
+
+ {#each Array(10) as _, i}
+ {@const rating = i + 1}
+
handleSelect(rating)}
+ title={String(rating)}
+ >
+
+
+ {/each}
+ {#if value !== null}
+
+
+
+
+
+ {/if}
+
+
+ 1
+ 5
+ 10
+
+
+
+
diff --git a/apps/todo/apps/web/src/lib/components/form/LabelSelector.svelte b/apps/todo/apps/web/src/lib/components/form/LabelSelector.svelte
new file mode 100644
index 000000000..86d8d644c
--- /dev/null
+++ b/apps/todo/apps/web/src/lib/components/form/LabelSelector.svelte
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+ {#if selectedIds.length === 0}
+ Labels auswählen...
+ {:else}
+
+ {#each selectedIds.slice(0, 3) as labelId}
+ {@const label = labelsStore.getById(labelId)}
+ {#if label}
+
+ {label.name}
+
+ {/if}
+ {/each}
+ {#if selectedIds.length > 3}
+ +{selectedIds.length - 3}
+ {/if}
+
+ {/if}
+
+
+
+
+
+ {#if showDropdown}
+
e.stopPropagation()} role="listbox">
+ {#each labelsStore.labels as label}
+
toggleLabel(label.id)}
+ role="option"
+ aria-selected={selectedIds.includes(label.id)}
+ >
+
+ {label.name}
+ {#if selectedIds.includes(label.id)}
+
+
+
+ {/if}
+
+ {/each}
+ {#if labelsStore.labels.length === 0}
+
Keine Labels vorhanden
+ {/if}
+
+ {/if}
+
+
+
diff --git a/apps/todo/apps/web/src/lib/components/form/PrioritySelector.svelte b/apps/todo/apps/web/src/lib/components/form/PrioritySelector.svelte
new file mode 100644
index 000000000..429081926
--- /dev/null
+++ b/apps/todo/apps/web/src/lib/components/form/PrioritySelector.svelte
@@ -0,0 +1,76 @@
+
+
+
+ {#each priorities as p}
+ onChange(p.value)}
+ >
+
+ {p.label}
+
+ {/each}
+
+
+
diff --git a/apps/todo/apps/web/src/lib/components/form/StorypointsSelector.svelte b/apps/todo/apps/web/src/lib/components/form/StorypointsSelector.svelte
new file mode 100644
index 000000000..553bc7305
--- /dev/null
+++ b/apps/todo/apps/web/src/lib/components/form/StorypointsSelector.svelte
@@ -0,0 +1,105 @@
+
+
+
+ {#each options as sp}
+
handleSelect(sp)}
+ >
+ {sp}
+
+ {/each}
+ {#if value !== null}
+
+
+
+
+
+ {/if}
+
+
+
diff --git a/apps/todo/apps/web/src/lib/components/form/index.ts b/apps/todo/apps/web/src/lib/components/form/index.ts
new file mode 100644
index 000000000..cd7e13ded
--- /dev/null
+++ b/apps/todo/apps/web/src/lib/components/form/index.ts
@@ -0,0 +1,5 @@
+export { default as PrioritySelector } from './PrioritySelector.svelte';
+export { default as StorypointsSelector } from './StorypointsSelector.svelte';
+export { default as DurationPicker } from './DurationPicker.svelte';
+export { default as FunRatingPicker } from './FunRatingPicker.svelte';
+export { default as LabelSelector } from './LabelSelector.svelte';