Replace inline button markup with `ui.Button` component calls for consistency and maintainability. Add filter menu component with dropdown functionality. Convert roadmap mode toggle from link-based to select dropdown. Include filter counter badge and clear filter actions.
520 lines
24 KiB
Text
520 lines
24 KiB
Text
package views
|
|
|
|
import taskmodel "xtablo-backend/internal/tasks"
|
|
import "xtablo-backend/internal/web/ui"
|
|
|
|
templ TasksPageContent(vm TasksPageViewModel) {
|
|
<div class="min-h-screen" data-current-view={ string(vm.State.View) }>
|
|
<div class="px-4 md:px-6 pt-6 md:pt-10 pb-5">
|
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4">
|
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">Mes Tâches</h1>
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: "Nouvelle tâche",
|
|
Variant: ui.ButtonVariantDefault,
|
|
Size: ui.SizeMD,
|
|
Type: "button",
|
|
Icon: "plus",
|
|
})
|
|
</div>
|
|
@TasksViewTabs(vm.State)
|
|
<div class="flex flex-col md:flex-row md:items-center md:justify-end gap-3">
|
|
@ui.Button(ui.ButtonProps{
|
|
Label: tasksFilterSummaryLabel(vm.Filters),
|
|
Variant: ui.ButtonVariantNeutral,
|
|
Tone: ui.ButtonToneSolid,
|
|
Size: ui.SizeMD,
|
|
Type: "button",
|
|
Icon: "filter",
|
|
Attrs: templ.Attributes{
|
|
"data-tasks-filter-trigger": "",
|
|
"aria-haspopup": "menu",
|
|
"aria-expanded": "false",
|
|
},
|
|
})
|
|
</div>
|
|
@TasksFilterMenu(vm)
|
|
</div>
|
|
<main class="px-4 md:px-6 pb-6">
|
|
if !vm.HasTasks {
|
|
<div class="rounded-[12px] border border-dashed border-[#D0D5DD] bg-[#F9FAFB] dark:bg-gray-800/60 p-8 text-center text-sm text-[#667085] dark:text-gray-400">
|
|
Aucune tâche pour le moment.
|
|
</div>
|
|
} else if vm.State.View == taskmodel.TaskViewList {
|
|
@TasksListLayout(vm.List, vm.State)
|
|
} else if vm.State.View == taskmodel.TaskViewRoadmap {
|
|
@TasksRoadmapLayout(vm.Roadmap, vm.State)
|
|
} else {
|
|
@TasksKanbanLayout(vm.Kanban, vm.State)
|
|
}
|
|
</main>
|
|
</div>
|
|
}
|
|
|
|
templ TasksFilterMenu(vm TasksPageViewModel) {
|
|
<div class="relative">
|
|
<form method="get" action="/tasks" class="hidden absolute right-0 top-2 z-50 w-56 max-h-[28rem] overflow-y-auto overflow-x-hidden rounded-md border bg-white dark:bg-gray-900 p-1 text-gray-900 dark:text-gray-100 shadow-md" data-tasks-filter-menu role="menu" aria-orientation="vertical">
|
|
<input type="hidden" name="view" value={ string(vm.State.View) }/>
|
|
if vm.State.View == taskmodel.TaskViewRoadmap {
|
|
<input type="hidden" name="roadmap_mode" value={ string(vm.State.RoadmapMode) }/>
|
|
}
|
|
<div class="px-2 py-1.5 text-sm font-semibold">Projet</div>
|
|
<div class="-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
<a href={ templ.SafeURL(tasksClearTabloFiltersHref(vm.State)) } class="relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
if tasksFilterGroupAllSelected(vm.Filters.Tablos) {
|
|
@TasksIcon("check", "h-4 w-4")
|
|
}
|
|
</span>
|
|
Tous les projets
|
|
</a>
|
|
for _, option := range vm.Filters.Tablos {
|
|
<label class="relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
<input class="sr-only" type="checkbox" name="tablo" value={ option.Value } if option.Selected {
|
|
checked
|
|
} onchange="this.form.requestSubmit()"/>
|
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
if option.Selected {
|
|
@TasksIcon("check", "h-4 w-4")
|
|
}
|
|
</span>
|
|
<div class="flex items-center gap-2">
|
|
<div class="w-2 h-2 rounded-full shrink-0 bg-blue-500"></div>
|
|
{ option.Label }
|
|
</div>
|
|
</label>
|
|
}
|
|
<div class="-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
<div class="px-2 py-1.5 text-sm font-semibold">Statut</div>
|
|
<div class="-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
<a href={ templ.SafeURL(tasksClearStatusFiltersHref(vm.State)) } class="relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
if tasksFilterGroupAllSelected(vm.Filters.Statuses) {
|
|
@TasksIcon("check", "h-4 w-4")
|
|
}
|
|
</span>
|
|
Tous
|
|
</a>
|
|
for _, option := range vm.Filters.Statuses {
|
|
<label class="relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
<input class="sr-only" type="checkbox" name="status" value={ option.Value } if option.Selected {
|
|
checked
|
|
} onchange="this.form.requestSubmit()"/>
|
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
if option.Selected {
|
|
@TasksIcon("check", "h-4 w-4")
|
|
}
|
|
</span>
|
|
{ option.Label }
|
|
</label>
|
|
}
|
|
<div class="-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
<div class="px-2 py-1.5 text-sm font-semibold">Assigné</div>
|
|
<div class="-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
<a href={ templ.SafeURL(tasksClearAssigneeFiltersHref(vm.State)) } class="relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
if tasksFilterGroupAllSelected(vm.Filters.Assignees) {
|
|
@TasksIcon("check", "h-4 w-4")
|
|
}
|
|
</span>
|
|
Tous
|
|
</a>
|
|
for _, option := range vm.Filters.Assignees {
|
|
<label class="relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
<input class="sr-only" type="checkbox" name="assignee" value={ option.Value } if option.Selected {
|
|
checked
|
|
} onchange="this.form.requestSubmit()"/>
|
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
if option.Selected {
|
|
@TasksIcon("check", "h-4 w-4")
|
|
}
|
|
</span>
|
|
{ option.Label }
|
|
</label>
|
|
}
|
|
<div class="-mx-1 my-1 h-px bg-gray-200 dark:bg-gray-700"></div>
|
|
<div class="p-1">
|
|
<a href={ templ.SafeURL(stateAction("/tasks", taskmodel.TaskPageState{View: vm.State.View, RoadmapMode: vm.State.RoadmapMode})) } class="block rounded-sm px-2 py-1.5 text-sm font-medium text-[#667085] hover:bg-gray-100 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100">Réinitialiser</a>
|
|
</div>
|
|
</form>
|
|
<script>
|
|
(function () {
|
|
if (window.__tasksFilterMenuInit) {
|
|
window.__tasksFilterMenuInit(document);
|
|
return;
|
|
}
|
|
|
|
window.__tasksFilterMenuInit = function (scope) {
|
|
(scope || document).querySelectorAll("[data-tasks-filter-trigger]").forEach(function (trigger) {
|
|
if (trigger.dataset.tasksFilterBound === "true") {
|
|
return;
|
|
}
|
|
trigger.dataset.tasksFilterBound = "true";
|
|
|
|
var shell = trigger.closest(".px-4, .md\\:px-6") || trigger.parentElement;
|
|
var menu = shell ? shell.querySelector("[data-tasks-filter-menu]") : null;
|
|
if (!menu) {
|
|
return;
|
|
}
|
|
|
|
trigger.addEventListener("click", function (event) {
|
|
event.preventDefault();
|
|
var open = !menu.classList.contains("hidden");
|
|
document.querySelectorAll("[data-tasks-filter-menu]").forEach(function (other) {
|
|
other.classList.add("hidden");
|
|
});
|
|
document.querySelectorAll("[data-tasks-filter-trigger]").forEach(function (otherTrigger) {
|
|
otherTrigger.setAttribute("aria-expanded", "false");
|
|
});
|
|
if (!open) {
|
|
menu.classList.remove("hidden");
|
|
trigger.setAttribute("aria-expanded", "true");
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
document.addEventListener("click", function (event) {
|
|
if (event.target.closest("[data-tasks-filter-menu]") || event.target.closest("[data-tasks-filter-trigger]")) {
|
|
return;
|
|
}
|
|
document.querySelectorAll("[data-tasks-filter-menu]").forEach(function (menu) {
|
|
menu.classList.add("hidden");
|
|
});
|
|
document.querySelectorAll("[data-tasks-filter-trigger]").forEach(function (trigger) {
|
|
trigger.setAttribute("aria-expanded", "false");
|
|
});
|
|
});
|
|
|
|
document.addEventListener("htmx:afterSwap", function (event) {
|
|
window.__tasksFilterMenuInit(event.target);
|
|
});
|
|
|
|
window.__tasksFilterMenuInit(document);
|
|
})();
|
|
</script>
|
|
</div>
|
|
}
|
|
|
|
templ TasksViewTabs(state taskmodel.TaskPageState) {
|
|
<div class="flex flex-wrap items-center gap-2 md:gap-6 mb-4 border-b border-[#EAECF0] dark:border-gray-700">
|
|
<a href={ templ.SafeURL(taskViewHref(state, taskmodel.TaskViewKanban)) } class={ taskViewTabClass(state, taskmodel.TaskViewKanban) } if state.View == taskmodel.TaskViewKanban {
|
|
aria-current="page"
|
|
}>
|
|
@TasksIcon("kanban", "w-4 h-4")
|
|
<span>Tableau</span>
|
|
</a>
|
|
<a href={ templ.SafeURL(taskViewHref(state, taskmodel.TaskViewList)) } class={ taskViewTabClass(state, taskmodel.TaskViewList) } if state.View == taskmodel.TaskViewList {
|
|
aria-current="page"
|
|
}>
|
|
@TasksIcon("list", "w-4 h-4")
|
|
<span>Liste</span>
|
|
</a>
|
|
<a href={ templ.SafeURL(taskViewHref(state, taskmodel.TaskViewRoadmap)) } class={ taskViewTabClass(state, taskmodel.TaskViewRoadmap) } if state.View == taskmodel.TaskViewRoadmap {
|
|
aria-current="page"
|
|
}>
|
|
@TasksIcon("map", "w-4 h-4")
|
|
<span>Roadmap</span>
|
|
</a>
|
|
<button type="button" disabled class="flex items-center gap-2 pb-3 pt-1 px-2 text-sm font-semibold transition-colors border-b-2 min-h-[44px] text-[#667085] border-transparent hover:text-gray-900 dark:hover:text-gray-100 cursor-not-allowed">
|
|
@TasksIcon("calendar", "lucide lucide-calendar w-4 h-4 opacity-40")
|
|
<span class="opacity-40">Calendrier</span>
|
|
<span class="text-[10px] font-medium px-1.5 py-0.5 rounded-full bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 leading-none">Bientôt</span>
|
|
</button>
|
|
</div>
|
|
if state.View == taskmodel.TaskViewRoadmap {
|
|
<div class="mb-4 max-w-[220px]">
|
|
@ui.Select(taskRoadmapModeSelectProps(state))
|
|
</div>
|
|
}
|
|
}
|
|
|
|
templ TasksKanbanLayout(view TasksKanbanView, state taskmodel.TaskPageState) {
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4" data-task-kanban>
|
|
for _, column := range view.Columns {
|
|
<div class="w-full h-fit bg-[#F9FAFB] dark:bg-gray-800/60 rounded-[12px] p-4" data-status-column={ column.ID }>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center space-x-2">
|
|
@TasksIcon("circle", "lucide lucide-circle w-5 h-5 "+statusIconClass(column.ID))
|
|
<h2 class="font-semibold text-gray-800 dark:text-gray-100">{ column.Label }</h2>
|
|
<span class="bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 text-xs font-medium px-2 py-0.5 rounded-full">{ len(column.Tasks) }</span>
|
|
</div>
|
|
@ui.IconButton(ui.IconButtonProps{
|
|
Label: "Nouvelle tâche dans " + column.Label,
|
|
Icon: "plus",
|
|
Variant: ui.IconButtonVariantNeutral,
|
|
Tone: ui.IconButtonToneGhost,
|
|
Type: "button",
|
|
})
|
|
</div>
|
|
<div class="space-y-3 pr-1 min-h-[80px]">
|
|
for _, task := range column.Tasks {
|
|
@TaskCard(task, state, false)
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
templ TasksListLayout(view TasksListView, state taskmodel.TaskPageState) {
|
|
<div class="space-y-6" data-task-list>
|
|
for _, group := range view.Groups {
|
|
<section class="rounded-[12px] bg-[#F9FAFB] dark:bg-gray-800/60 p-4" data-status-group={ group.ID }>
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center gap-2">
|
|
@TasksIcon("circle", "lucide lucide-circle w-5 h-5 "+statusIconClass(group.ID))
|
|
<h2 class="font-semibold text-gray-800 dark:text-gray-100">{ group.Label }</h2>
|
|
<span class="bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-300 text-xs font-medium px-2 py-0.5 rounded-full">{ len(group.Tasks) }</span>
|
|
</div>
|
|
@ui.IconButton(ui.IconButtonProps{
|
|
Label: "Nouvelle tâche dans " + group.Label,
|
|
Icon: "plus",
|
|
Variant: ui.IconButtonVariantNeutral,
|
|
Tone: ui.IconButtonToneGhost,
|
|
Type: "button",
|
|
})
|
|
</div>
|
|
<div class="space-y-3">
|
|
for _, task := range group.Tasks {
|
|
@TasksListRow(task, state)
|
|
}
|
|
</div>
|
|
</section>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
templ TasksRoadmapLayout(view TasksRoadmapView, state taskmodel.TaskPageState) {
|
|
<div class="space-y-6" data-task-roadmap>
|
|
for _, lane := range view.Lanes {
|
|
<section class="rounded-[12px] bg-[#F9FAFB] dark:bg-gray-800/60 p-4" data-roadmap-lane={ lane.ID }>
|
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3 mb-4">
|
|
<div>
|
|
<h2 class="font-semibold text-gray-800 dark:text-gray-100">{ lane.Label }</h2>
|
|
<p class="text-xs text-[#667085] dark:text-gray-400">Étape comme lane horizontale, avec bucketisation par date d'échéance.</p>
|
|
</div>
|
|
@ui.IconButton(ui.IconButtonProps{
|
|
Label: "Nouvelle tâche dans " + lane.Label,
|
|
Icon: "plus",
|
|
Variant: ui.IconButtonVariantNeutral,
|
|
Tone: ui.IconButtonToneGhost,
|
|
Type: "button",
|
|
})
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
|
for _, bucket := range lane.Buckets {
|
|
<div class="rounded-[12px] bg-white dark:bg-gray-900 border border-[#EAECF0] dark:border-gray-700 p-4" data-roadmap-bucket={ bucket.ID }>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<h3 class="text-sm font-semibold text-gray-800 dark:text-gray-100">{ bucket.Label }</h3>
|
|
<span class="text-[11px] font-medium px-2 py-0.5 rounded-full bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400">{ len(bucket.Tasks) }</span>
|
|
</div>
|
|
<div class="space-y-3 min-h-[56px]">
|
|
for _, task := range bucket.Tasks {
|
|
@TaskCard(task, state, false)
|
|
}
|
|
</div>
|
|
</div>
|
|
}
|
|
</div>
|
|
</section>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
templ TaskCard(task TaskCardView, state taskmodel.TaskPageState, compact bool) {
|
|
<article draggable="true" class={ taskCardClass(compact) } data-task-id={ task.ID }>
|
|
<div class="flex items-start justify-between gap-2 mb-2">
|
|
<h3 class="font-semibold text-gray-900 dark:text-gray-100 text-sm leading-tight line-clamp-2 flex-1">{ task.Title }</h3>
|
|
@ui.IconButton(ui.IconButtonProps{
|
|
Label: "Modifier la tâche " + task.Title,
|
|
Icon: "pencil",
|
|
Variant: ui.IconButtonVariantNeutral,
|
|
Tone: ui.IconButtonToneGhost,
|
|
Type: "button",
|
|
Attrs: templ.Attributes{
|
|
"hx-get": taskEditHref(task, state),
|
|
"hx-target": "#app-main-content",
|
|
"hx-swap": "beforeend",
|
|
},
|
|
})
|
|
@ui.IconButton(ui.IconButtonProps{
|
|
Label: taskDeleteAriaLabel(task),
|
|
Icon: "trash",
|
|
Variant: ui.IconButtonVariantDanger,
|
|
Tone: ui.IconButtonToneGhost,
|
|
Type: "button",
|
|
Attrs: templ.Attributes{
|
|
"hx-delete": taskDeleteHref(task, state),
|
|
"hx-target": "#app-main-content",
|
|
"hx-swap": "outerHTML",
|
|
},
|
|
})
|
|
</div>
|
|
if task.DueDate != "" {
|
|
<div class={ "flex items-center text-xs mb-3 " + dueDateToneClass(task.DueDateValue) }>
|
|
@TasksIcon("calendar", "lucide lucide-calendar w-3.5 h-3.5 mr-1.5")
|
|
{ task.DueDate }
|
|
</div>
|
|
}
|
|
<div class="flex items-center mb-3 border-b border-dashed border-[#D0D5DD] dark:border-gray-600 pb-3">
|
|
<div class={ "w-5 h-5 rounded-[5px] mr-2 flex items-center justify-center shrink-0 " + etapeBadgeClass(task) }>
|
|
@TasksIcon(etapeIconName(task), "w-3 h-3 text-white")
|
|
</div>
|
|
<span class="text-xs text-gray-600 dark:text-gray-400 truncate">{ etapeLabel(task) }</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center space-x-3 text-gray-500 dark:text-gray-400">
|
|
<div class="flex items-center text-xs">
|
|
@TasksIcon("message-square", "lucide lucide-message-square w-3.5 h-3.5 mr-1")
|
|
0
|
|
</div>
|
|
<div class="flex items-center text-xs">
|
|
@TasksIcon("paperclip", "lucide lucide-paperclip w-3.5 h-3.5 mr-1")
|
|
0
|
|
</div>
|
|
<div class="flex items-center text-xs">
|
|
<span class="font-medium text-gray-700 dark:text-gray-300">Tablo</span> { task.TabloName }
|
|
</div>
|
|
</div>
|
|
<div class="flex -space-x-2">
|
|
@TaskAssigneeAvatar(task)
|
|
</div>
|
|
</div>
|
|
</article>
|
|
}
|
|
|
|
templ TasksListRow(task TaskCardView, state taskmodel.TaskPageState) {
|
|
<div class="bg-white dark:bg-gray-900 rounded-[12px] border border-[#EAECF0] dark:border-gray-700 p-4 shadow-sm" data-task-id={ task.ID }>
|
|
<div class="grid grid-cols-1 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)] gap-4">
|
|
@TaskCard(task, state, true)
|
|
<div class="rounded-lg bg-[#F9FAFB] dark:bg-gray-800/60 p-4 text-sm text-gray-700 dark:text-gray-300">
|
|
<dl class="space-y-3">
|
|
<div>
|
|
<dt class="text-xs font-semibold uppercase tracking-wide text-gray-500">Tablo</dt>
|
|
<dd class="mt-1">{ task.TabloName }</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-xs font-semibold uppercase tracking-wide text-gray-500">Étape</dt>
|
|
<dd class="mt-1">{ etapeLabel(task) }</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-xs font-semibold uppercase tracking-wide text-gray-500">Assignée</dt>
|
|
<dd class="mt-1">{ emptyFallback(task.Assignee, "Non assignée") }</dd>
|
|
</div>
|
|
<div>
|
|
<dt class="text-xs font-semibold uppercase tracking-wide text-gray-500">Statut</dt>
|
|
<dd class="mt-1">{ task.StatusLabel }</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
templ TaskAssigneeAvatar(task TaskCardView) {
|
|
if taskHasAssignee(task) {
|
|
<div class="w-6 h-6 rounded-full bg-purple-500 border-2 border-white dark:border-gray-800 flex items-center justify-center text-white text-[10px] font-medium">
|
|
{ assigneeInitials(task.Assignee) }
|
|
</div>
|
|
} else {
|
|
<div class="w-6 h-6 rounded-full bg-gray-200 dark:bg-gray-600 border-2 border-white dark:border-gray-800 flex items-center justify-center">
|
|
@TasksIcon("user", "lucide lucide-user w-3 h-3 text-gray-400 dark:text-gray-300")
|
|
</div>
|
|
}
|
|
}
|
|
|
|
templ TasksIcon(kind string, className string) {
|
|
switch kind {
|
|
case "circle":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
</svg>
|
|
case "plus":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M5 12h14"></path>
|
|
<path d="M12 5v14"></path>
|
|
</svg>
|
|
case "kanban":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M6 5v11"></path>
|
|
<path d="M12 5v6"></path>
|
|
<path d="M18 5v14"></path>
|
|
</svg>
|
|
case "list":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M3 12h.01"></path>
|
|
<path d="M3 18h.01"></path>
|
|
<path d="M3 6h.01"></path>
|
|
<path d="M8 12h13"></path>
|
|
<path d="M8 18h13"></path>
|
|
<path d="M8 6h13"></path>
|
|
</svg>
|
|
case "map":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M14.106 5.553a2 2 0 0 0 1.788 0l3.659-1.83A1 1 0 0 1 21 4.619v12.764a1 1 0 0 1-.553.894l-4.553 2.277a2 2 0 0 1-1.788 0l-4.212-2.106a2 2 0 0 0-1.788 0l-3.659 1.83A1 1 0 0 1 3 19.381V6.618a1 1 0 0 1 .553-.894l4.553-2.277a2 2 0 0 1 1.788 0z"></path>
|
|
<path d="M15 5.764v15"></path>
|
|
<path d="M9 3.236v15"></path>
|
|
</svg>
|
|
case "calendar":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M8 2v4"></path>
|
|
<path d="M16 2v4"></path>
|
|
<rect width="18" height="18" x="3" y="4" rx="2"></rect>
|
|
<path d="M3 10h18"></path>
|
|
</svg>
|
|
case "settings2":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M20 7h-9"></path>
|
|
<path d="M14 17H5"></path>
|
|
<circle cx="17" cy="17" r="3"></circle>
|
|
<circle cx="7" cy="7" r="3"></circle>
|
|
</svg>
|
|
case "ellipsis-vertical":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<circle cx="12" cy="12" r="1"></circle>
|
|
<circle cx="12" cy="5" r="1"></circle>
|
|
<circle cx="12" cy="19" r="1"></circle>
|
|
</svg>
|
|
case "trash2":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M3 6h18"></path>
|
|
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
|
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
|
<line x1="10" x2="10" y1="11" y2="17"></line>
|
|
<line x1="14" x2="14" y1="11" y2="17"></line>
|
|
</svg>
|
|
case "message-square":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
</svg>
|
|
case "paperclip":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"></path>
|
|
</svg>
|
|
case "user":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"></path>
|
|
<circle cx="12" cy="7" r="4"></circle>
|
|
</svg>
|
|
case "gem":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M6 3h12l4 6-10 13L2 9Z"></path>
|
|
<path d="M11 3 8 9l4 13 4-13-3-6"></path>
|
|
<path d="M2 9h20"></path>
|
|
</svg>
|
|
case "flame":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"></path>
|
|
</svg>
|
|
case "zap":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"></path>
|
|
</svg>
|
|
case "check":
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class={ className }>
|
|
<path d="M20 6 9 17l-5-5"></path>
|
|
</svg>
|
|
default:
|
|
@TasksIcon("circle", className)
|
|
}
|
|
}
|