feat: project colors, timeline filters, sidebar fix, GitLooper agent, and misc improvements
- Fix sidebar double-highlight on /vacations/my (Gitea #6): add isNavItemActive() helper - Add project color picker (schema + API + modal + timeline rendering) - Add ProjectCombobox/ResourceCombobox to timeline toolbar - Show PENDING vacations on timeline with dashed/dimmed style - Add "show demand projects" preference with localStorage persistence - Add ProjectAssignmentsTable with total hours/cost columns - Extend vacation API to accept status arrays - Add GitLooper formal YAML agent configuration - Extend user admin with permission overrides UI - Add delete-assignment use case tests - Add status-styles.ts shared badge constants - Centralize formatMoney/formatCents in format.ts Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -46,6 +46,7 @@ interface FormState {
|
||||
endDate: string;
|
||||
status: string;
|
||||
responsiblePerson: string;
|
||||
color: string;
|
||||
utilizationCategoryId: string;
|
||||
clientId: string;
|
||||
}
|
||||
@@ -63,6 +64,7 @@ function getDefaultForm(): FormState {
|
||||
endDate: today,
|
||||
status: "DRAFT",
|
||||
responsiblePerson: "",
|
||||
color: "",
|
||||
utilizationCategoryId: "",
|
||||
clientId: "",
|
||||
};
|
||||
@@ -80,6 +82,7 @@ function projectToForm(project: Project): FormState {
|
||||
endDate: formatDateForInput(project.endDate),
|
||||
status: project.status,
|
||||
responsiblePerson: project.responsiblePerson ?? "",
|
||||
color: (project as unknown as { color?: string | null }).color ?? "",
|
||||
utilizationCategoryId: (project as unknown as { utilizationCategoryId?: string | null }).utilizationCategoryId ?? "",
|
||||
clientId: (project as unknown as { clientId?: string | null }).clientId ?? "",
|
||||
};
|
||||
@@ -201,6 +204,7 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
|
||||
endDate: new Date(form.endDate),
|
||||
status: form.status as unknown as ProjectStatus,
|
||||
responsiblePerson: form.responsiblePerson.trim() || undefined,
|
||||
...(form.color ? { color: form.color } : {}),
|
||||
...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}),
|
||||
...(form.clientId ? { clientId: form.clientId } : {}),
|
||||
},
|
||||
@@ -219,6 +223,7 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
|
||||
staffingReqs: [],
|
||||
dynamicFields: {},
|
||||
responsiblePerson: form.responsiblePerson.trim() || undefined,
|
||||
...(form.color ? { color: form.color } : {}),
|
||||
...(form.utilizationCategoryId ? { utilizationCategoryId: form.utilizationCategoryId } : {}),
|
||||
...(form.clientId ? { clientId: form.clientId } : {}),
|
||||
});
|
||||
@@ -515,6 +520,32 @@ export function ProjectModal({ project, onClose }: ProjectModalProps) {
|
||||
className={inputClass}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className={labelClass} htmlFor="projectColor">
|
||||
Timeline Color
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
id="projectColor"
|
||||
type="color"
|
||||
value={form.color || "#3b82f6"}
|
||||
onChange={(e) => setField("color", e.target.value)}
|
||||
className="w-10 h-10 rounded-lg border border-gray-300 dark:border-gray-600 cursor-pointer p-0.5"
|
||||
/>
|
||||
<span className="text-xs text-gray-400 dark:text-gray-500">
|
||||
{form.color || "Default"}
|
||||
</span>
|
||||
{form.color && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setField("color", "")}
|
||||
className="text-xs text-gray-400 hover:text-red-500"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user