diff --git a/app/Application/BillingService.php b/app/Application/BillingService.php index fc3216d..979138f 100644 --- a/app/Application/BillingService.php +++ b/app/Application/BillingService.php @@ -47,7 +47,7 @@ public function __construct(private readonly CompanyDataReader $companyData) {} * * @param string|null $from inclusive start date, Y-m-d * @param string|null $to inclusive end date, Y-m-d - * @return array{customer_id: int, from: string|null, to: string|null, entry_ids: list, minutes: int, currencies: list, groups: array{task: list>, project: list>, member: list>, summary: list>}} + * @return array{customer_id: int, from: string|null, to: string|null, entry_ids: list, minutes: int, currencies: list, entries: list>, groups: array{task: list>, project: list>, member: list>, summary: list>}} */ public function unbilled(int $companyId, int $customerId, ?string $from = null, ?string $to = null): array { @@ -72,6 +72,7 @@ public function unbilled(int $companyId, int $customerId, ?string $from = null, 'entry_ids' => $entries->map(static fn (TimeEntry $entry): int => (int) $entry->id)->all(), 'minutes' => $minutes, 'currencies' => array_values($currencies), + 'entries' => $this->rows($entries, $labels), 'groups' => [ 'task' => $this->group($entries, 'task', $labels, true), 'project' => $this->group($entries, 'project', $labels, true), @@ -81,6 +82,59 @@ public function unbilled(int $companyId, int $customerId, ?string $from = null, ]; } + /** + * Which customers have unbilled billable time, and how much of it. + * + * The same rule `unbilled()` applies to one customer, applied to all of + * them at once: the wizard's first step needs to know who is worth opening + * before it asks for anyone's entries. A customer whose work spans two + * currencies gets a row per currency, because money in two denominations + * cannot be added up and `prepare()` refuses such a selection anyway. + * + * @param string|null $from inclusive start date, Y-m-d + * @param string|null $to inclusive end date, Y-m-d + * @return list + */ + public function customers(int $companyId, ?string $from = null, ?string $to = null): array + { + $customerByTask = Task::query() + ->forCompany($companyId) + ->whereNotNull('customer_id') + ->pluck('customer_id', 'id'); + + $entries = $this->unbilledEntriesForTasks( + $companyId, + array_map(intval(...), $customerByTask->keys()->all()), + $from, + $to, + ); + + $rows = []; + foreach ($entries as $entry) { + $customerId = (int) $customerByTask->get((int) $entry->task_id); + $currencyId = $entry->currency_id === null ? null : (int) $entry->currency_id; + + $bucket = $customerId.'|'.($currencyId ?? 'null'); + $rows[$bucket] ??= [ + 'customer_id' => $customerId, + 'entries' => 0, + 'minutes' => 0, + 'amount' => 0, + 'currency_id' => $currencyId, + ]; + + $rows[$bucket]['entries']++; + $rows[$bucket]['minutes'] += (int) $entry->duration_minutes; + $rows[$bucket]['amount'] += (int) $entry->amount; + } + + $rows = array_values($rows); + usort($rows, static fn (array $left, array $right): int => [$left['customer_id'], $left['currency_id'] ?? 0] + <=> [$right['customer_id'], $right['currency_id'] ?? 0]); + + return $rows; + } + /** * The invoice body for a selection of entries, plus the entry ids behind * each line. @@ -91,9 +145,15 @@ public function unbilled(int $companyId, int $customerId, ?string $from = null, * blended rate when they differ, and its `total` is `quantity * price` so * the invoice the host builds matches the preview exactly. * + * Every key the host's invoice writer reads is present, including the ones + * this module never sets: a line carries its zeroed discount and tax fields + * so `DocumentItemService::createItems` never reaches for a missing index, + * and `notes` and `template_name` are placeholders the wizard fills in from + * the company's own defaults before it posts. + * * @param list $entryIds * @param 'task'|'project'|'member'|'summary' $grouping - * @return array{invoice_date: string, customer_id: int, currency_id: int|null, discount: int, discount_type: string, discount_val: int, tax: int, sub_total: int, total: int, items: list, groups: list}>} + * @return array{invoice_date: string, customer_id: int, currency_id: int|null, discount: int, discount_type: string, discount_val: int, tax: int, sub_total: int, total: int, notes: string|null, template_name: string|null, taxes: list>, items: list>, total: int}>, groups: list}>} */ public function prepare(int $companyId, array $entryIds, string $grouping): array { @@ -121,6 +181,11 @@ public function prepare(int $companyId, array $entryIds, string $grouping): arra 'description' => $row['description'], 'quantity' => $quantity, 'price' => $price, + 'discount_type' => 'fixed', + 'discount' => 0, + 'discount_val' => 0, + 'tax' => 0, + 'taxes' => [], 'total' => $total, ]; $groups[] = ['entry_ids' => $row['entry_ids']]; @@ -137,6 +202,9 @@ public function prepare(int $companyId, array $entryIds, string $grouping): arra 'tax' => 0, 'sub_total' => $subTotal, 'total' => $subTotal, + 'notes' => null, + 'template_name' => null, + 'taxes' => [], 'items' => $items, 'groups' => $groups, ]; @@ -212,6 +280,21 @@ private function unbilledEntries(int $companyId, int $customerId, ?string $from, ->pluck('id') ->all(); + return $this->unbilledEntriesForTasks($companyId, array_map(intval(...), $taskIds), $from, $to); + } + + /** + * The billable, stopped, not-yet-invoiced time logged against these tasks. + * + * One customer's list and the whole company's list differ only in which + * tasks go in, so both ask this: the internal-project exclusion, the date + * range and the vanished-invoice rule are written once. + * + * @param list $taskIds + * @return Collection + */ + private function unbilledEntriesForTasks(int $companyId, array $taskIds, ?string $from, ?string $to): Collection + { if ($taskIds === []) { /** @var Collection $none */ $none = new Collection; @@ -379,6 +462,38 @@ private function singleCurrencyFor(Collection $entries): ?int return $currencyId === null ? null : (int) $currencyId; } + /** + * One row per entry, with the names the review step shows. + * + * The grouped views answer "how much"; this answers "which work", so the + * step that ticks entries off can render the task, the project, the member + * and the day without a second round trip per row. + * + * @param Collection $entries + * @param array{task: array, project: array, member: array} $labels + * @return list + */ + private function rows(Collection $entries, array $labels): array + { + return $entries->map(static fn (TimeEntry $entry): array => [ + 'id' => (int) $entry->id, + 'task_id' => (int) $entry->task_id, + 'task_name' => $labels['task'][(int) $entry->task_id] ?? "Task {$entry->task_id}", + 'project_id' => $entry->project_id === null ? null : (int) $entry->project_id, + 'project_name' => $entry->project_id === null + ? null + : ($labels['project'][(int) $entry->project_id] ?? "Project {$entry->project_id}"), + 'user_id' => (int) $entry->user_id, + 'user_name' => $labels['member'][(int) $entry->user_id] ?? 'Removed member', + 'date' => $entry->started_at?->toDateString(), + 'minutes' => (int) $entry->duration_minutes, + 'amount' => (int) $entry->amount, + 'rate' => (int) $entry->rate, + 'currency_id' => $entry->currency_id === null ? null : (int) $entry->currency_id, + 'description' => $entry->description, + ])->values()->all(); + } + /** * Human labels for every grouping key the entries touch. * diff --git a/app/Http/Controllers/BillingController.php b/app/Http/Controllers/BillingController.php index 5d81963..7a92de3 100644 --- a/app/Http/Controllers/BillingController.php +++ b/app/Http/Controllers/BillingController.php @@ -8,6 +8,7 @@ use Modules\TasksProjects\Application\BillingService; use Modules\TasksProjects\Http\Requests\ConfirmInvoiceRequest; use Modules\TasksProjects\Http\Requests\PrepareInvoiceRequest; +use Modules\TasksProjects\Http\Requests\UnbilledCustomersRequest; use Modules\TasksProjects\Http\Requests\UnbilledTimeRequest; use Modules\TasksProjects\Support\Abilities; use Modules\TasksProjects\Support\Authorizes; @@ -27,6 +28,27 @@ public function __construct(Authorizes $authorizes, private readonly BillingServ parent::__construct($authorizes); } + /** + * Who has unbilled time, before the wizard asks for anyone's entries. + * + * One row per customer and currency, so the first step can be a list of + * people worth invoicing rather than a customer picker over the whole + * address book. + */ + public function customers(UnbilledCustomersRequest $request): JsonResponse + { + $context = $this->context($request); + $this->authorize($context, Abilities::INVOICE_TASKS); + + $filters = $request->validated(); + + return response()->json(['data' => $this->billing->customers( + $context->companyId, + $filters['from'] ?? null, + $filters['to'] ?? null, + )]); + } + public function unbilled(UnbilledTimeRequest $request): JsonResponse { $context = $this->context($request); diff --git a/app/Http/Requests/UnbilledCustomersRequest.php b/app/Http/Requests/UnbilledCustomersRequest.php new file mode 100644 index 0000000..cb6c0f3 --- /dev/null +++ b/app/Http/Requests/UnbilledCustomersRequest.php @@ -0,0 +1,23 @@ +> */ + public function rules(): array + { + return [ + 'from' => ['sometimes', 'date'], + 'to' => ['sometimes', 'date'], + ]; + } +} diff --git a/dist/init.js b/dist/init.js index 34b2120..e943ed0 100644 --- a/dist/init.js +++ b/dist/init.js @@ -89,59 +89,59 @@ async function L(e, t, n) { let { data: r } = await e.put(M.project(t), n); return r.data; } -async function ee(e, t) { +async function R(e, t) { let { data: n } = await e.post(M.archiveProject(t)); return n.data; } -async function R(e, t) { +async function z(e, t) { let { data: n } = await e.post(M.unarchiveProject(t)); return n.data; } -async function z(e, t) { +async function B(e, t) { await e.delete(M.project(t)); } -async function te(e) { +async function V(e) { let { data: t } = await e.get(M.members); return t.data; } -async function B(e, t = 100) { +async function H(e, t = 100) { let { data: n } = await e.get(N.customers, { params: { limit: t } }); return n.data; } //#endregion //#region resources/js/support/errors.ts -function ne(e) { +function ee(e) { if (typeof e != "object" || !e) return null; let t = e.response; return typeof t?.data != "object" || t.data === null ? null : t.data; } -function V(e, t) { - let n = ne(e)?.message; +function U(e, t) { + let n = ee(e)?.message; return typeof n == "string" && n !== "" ? n : t; } -function re(e) { - let t = ne(e)?.errors, n = {}; +function te(e) { + let t = ee(e)?.errors, n = {}; if (typeof t != "object" || !t) return n; for (let [e, r] of Object.entries(t)) Array.isArray(r) && typeof r[0] == "string" && (n[e] = r[0]); return n; } //#endregion //#region resources/js/support/format.ts -function ie(e) { +function ne(e) { return e === null ? "" : String(e / 100); } -function ae(e) { +function re(e) { let t = Number(e); return e.trim() === "" || Number.isNaN(t) ? null : Math.round(t * 100); } -function oe(e) { +function ie(e) { return e === null ? "" : String(e / 60); } -function se(e) { +function ae(e) { let t = Number(e); return e.trim() === "" || Number.isNaN(t) ? null : Math.round(t * 60); } -function ce(e) { +function oe(e) { if (!e) return ""; let [t, n, r] = e.slice(0, 10).split("-").map(Number); return !t || !n || !r ? e : new Date(Date.UTC(t, n - 1, r)).toLocaleDateString(void 0, { @@ -151,32 +151,32 @@ function ce(e) { timeZone: "UTC" }); } -function le(e) { +function se(e) { if (typeof e == "string") return e.slice(0, 10); let t = String(e.getMonth() + 1).padStart(2, "0"), n = String(e.getDate()).padStart(2, "0"); return `${e.getFullYear()}-${t}-${n}`; } -function ue(e) { +function ce(e) { let t = Math.max(0, Math.round(e ?? 0)), n = Math.floor(t / 60), r = t % 60; return n === 0 ? `${r}m` : r === 0 ? `${n}h` : `${n}h ${r}m`; } -function de(e) { +function le(e) { let t = e.trim().split(/\s+/).filter(Boolean); return t.length === 0 ? "?" : (t[0].charAt(0) + (t.length > 1 ? t[t.length - 1].charAt(0) : "")).toUpperCase(); } -function fe(e) { +function ue(e) { if (!e) return !1; let t = /* @__PURE__ */ new Date(), n = `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, "0")}-${String(t.getDate()).padStart(2, "0")}`; return e.slice(0, 10) < n; } //#endregion //#region resources/js/support/i18n.ts -function H() { +function de() { return u()?.appContext.config.globalProperties.$t ?? ((e) => e); } //#endregion //#region resources/js/components/ProjectFormModal.vue?vue&type=script&setup=true&lang.ts -var pe = { class: "flex w-full items-center justify-between" }, me = { class: "space-y-5 px-6 py-6" }, he = { class: "flex flex-wrap items-center gap-2" }, ge = ["aria-label", "onClick"], _e = { class: "flex justify-end space-x-3 border-t border-line-default px-6 py-4" }, ve = /* @__PURE__ */ l({ +var fe = { class: "flex w-full items-center justify-between" }, pe = { class: "space-y-5 px-6 py-6" }, me = { class: "flex flex-wrap items-center gap-2" }, he = ["aria-label", "onClick"], ge = { class: "flex justify-end space-x-3 border-t border-line-default px-6 py-4" }, _e = /* @__PURE__ */ l({ __name: "ProjectFormModal", props: { show: { type: Boolean }, @@ -195,7 +195,7 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s "#dc2626", "#7c3aed", "#64748b" - ], m = H(), h = _({ + ], m = de(), h = _({ name: "", identifier: "", description: "", @@ -205,11 +205,11 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s dueDate: "" }), C = v(null), w = v([]), D = v(!1), O = v({}), A = v(!1), j = n(() => l.project !== null), M = n(() => j.value ? m("tasks_projects.projects.edit_project") : m("tasks_projects.projects.new_project")); T(() => l.show, (e) => { - e && (N(), ee()); + e && (N(), R()); }, { immediate: !0 }); function N() { let e = l.project; - h.name = e?.name ?? "", h.identifier = e?.identifier ?? "", h.description = e?.description ?? "", h.colour = e?.colour ?? "", h.defaultRate = ie(e?.default_rate ?? null), h.budgetHours = oe(e?.budget_minutes ?? null), h.dueDate = e?.due_date ?? "", O.value = {}, C.value = P(e?.customer_id ?? null); + h.name = e?.name ?? "", h.identifier = e?.identifier ?? "", h.description = e?.description ?? "", h.colour = e?.colour ?? "", h.defaultRate = ne(e?.default_rate ?? null), h.budgetHours = ie(e?.budget_minutes ?? null), h.dueDate = e?.due_date ?? "", O.value = {}, C.value = P(e?.customer_id ?? null); } function P(e) { return e === null ? null : w.value.find((t) => t.id === e) ?? null; @@ -217,33 +217,33 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s function F(e) { return e.display_name || e.name || `#${e.id}`; } - async function ee() { + async function R() { if (!D.value) try { - let e = await B(l.client); + let e = await H(l.client); w.value = e.map((e) => ({ id: e.id, label: F(e) })), D.value = !0, C.value = P(l.project?.customer_id ?? null); } catch (e) { - l.notify("error", V(e, m("tasks_projects.projects.customers_failed"))); + l.notify("error", U(e, m("tasks_projects.projects.customers_failed"))); } } - function R() { + function z() { return { name: h.name.trim(), customer_id: C.value?.id ?? null, identifier: h.identifier.trim() || null, description: h.description.trim() || null, colour: h.colour || null, - default_rate: ae(h.defaultRate), - budget_minutes: se(h.budgetHours), + default_rate: re(h.defaultRate), + budget_minutes: ae(h.budgetHours), due_date: h.dueDate || null }; } - function z(e) { - h.dueDate = e ? le(e) : ""; + function B(e) { + h.dueDate = e ? se(e) : ""; } - async function te() { + async function V() { if (!A.value) { if (h.name.trim() === "") { O.value = { name: m("tasks_projects.projects.name_required") }; @@ -251,10 +251,10 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s } A.value = !0, O.value = {}; try { - let e = l.project, t = e ? await L(l.client, e.id, R()) : await I(l.client, R()); + let e = l.project, t = e ? await L(l.client, e.id, z()) : await I(l.client, z()); u("saved", t); } catch (e) { - O.value = re(e), l.notify("error", V(e, m("tasks_projects.projects.save_failed"))); + O.value = te(e), l.notify("error", U(e, m("tasks_projects.projects.save_failed"))); } finally { A.value = !1; } @@ -266,12 +266,12 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s show: t.show, onClose: i[9] ||= (e) => u("close") }, { - header: E(() => [o("div", pe, [o("span", null, x(M.value), 1), c(l, { + header: E(() => [o("div", fe, [o("span", null, x(M.value), 1), c(l, { name: "XMarkIcon", class: "h-6 w-6 cursor-pointer text-subtle hover:text-body", onClick: i[0] ||= (e) => u("close") })])]), - default: E(() => [o("form", { onSubmit: k(te, ["prevent"]) }, [o("div", me, [ + default: E(() => [o("form", { onSubmit: k(V, ["prevent"]) }, [o("div", pe, [ c(N, null, { default: E(() => [ c(v, { @@ -333,7 +333,7 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s }, { default: E(() => [c(D, { "model-value": h.dueDate, - "onUpdate:modelValue": z + "onUpdate:modelValue": B }, null, 8, ["model-value"])]), _: 1 }, 8, ["label", "error"]), @@ -377,14 +377,14 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s label: S(m)("tasks_projects.projects.fields.colour"), error: O.value.colour }, { - default: E(() => [o("div", he, [(g(), a(e, null, y(d, (e) => o("button", { + default: E(() => [o("div", me, [(g(), a(e, null, y(d, (e) => o("button", { key: e, type: "button", class: f(["h-7 w-7 rounded-full border-2 transition", h.colour === e ? "border-heading" : "border-line-default"]), style: p({ backgroundColor: e }), "aria-label": e, onClick: (t) => h.colour = h.colour === e ? "" : e - }, null, 14, ge)), 64)), o("button", { + }, null, 14, he)), 64)), o("button", { type: "button", class: "rounded-md border border-line-default px-2 py-1 text-xs text-muted hover:bg-hover", onClick: i[6] ||= (e) => h.colour = "" @@ -403,7 +403,7 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s }, null, 8, ["modelValue", "invalid"])]), _: 1 }, 8, ["label", "error"]) - ]), o("div", _e, [c(F, { + ]), o("div", ge, [c(F, { type: "button", variant: "primary-outline", onClick: i[8] ||= (e) => u("close") @@ -423,29 +423,29 @@ var pe = { class: "flex w-full items-center justify-between" }, me = { class: "s }, 8, ["show"]); }; } -}), ye = _({}), be = !1, xe = null; -function Se(e) { - return e === null ? "" : ye[e] ?? `#${e}`; +}), ve = _({}), ye = !1, be = null; +function xe(e) { + return e === null ? "" : ve[e] ?? `#${e}`; } -async function Ce(e) { - be || (xe ??= Te(e), await xe); +async function Se(e) { + ye || (be ??= we(e), await be); } -function we() { - for (let e of Object.keys(ye)) delete ye[Number(e)]; - be = !1, xe = null; +function Ce() { + for (let e of Object.keys(ve)) delete ve[Number(e)]; + ye = !1, be = null; } -async function Te(e) { +async function we(e) { try { - for (let t of await B(e, 200)) { + for (let t of await H(e, 200)) { let e = t?.id; - typeof e == "number" && (ye[e] = Ee(t)); + typeof e == "number" && (ve[e] = Te(t)); } - be = !0; + ye = !0; } catch {} finally { - xe = null; + be = null; } } -function Ee(e) { +function Te(e) { let t = typeof e.display_name == "string" ? e.display_name.trim() : ""; if (t !== "") return t; let n = typeof e.name == "string" ? e.name.trim() : ""; @@ -453,19 +453,19 @@ function Ee(e) { } //#endregion //#region resources/js/pages/ProjectsIndexPage.vue?vue&type=script&setup=true&lang.ts -var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "relative table-container" }, ke = { class: "flex items-center" }, Ae = { +var Ee = { class: "flex items-center justify-end space-x-5" }, De = { class: "relative table-container" }, Oe = { class: "flex items-center" }, ke = { key: 0, class: "block text-xs font-normal text-muted" -}, je = { key: 0 }, Me = { +}, Ae = { key: 0 }, je = { key: 1, class: "text-subtle" -}, Ne = { +}, Me = { key: 1, class: "text-subtle" -}, Pe = { key: 0 }, Fe = { +}, Ne = { key: 0 }, Pe = { key: 1, class: "text-subtle" -}, Ie = 10, Le = 350, Re = /* @__PURE__ */ l({ +}, Fe = 10, Ie = 350, Le = /* @__PURE__ */ l({ __name: "ProjectsIndexPage", props: { client: { type: [Function, Object] }, @@ -478,7 +478,7 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re status: "status", default_rate: "default_rate", due_date: "due_date" - }, u = H(), d = v(null), h = v(!1), y = v(!0), C = v(0), O = v(!1), k = v(null), A = v(null), j = _({ + }, u = de(), d = v(null), h = v(!1), y = v(!0), C = v(0), O = v(!1), k = v(null), A = v(null), j = _({ search: "", status: "ACTIVE" }), M = n(() => [ @@ -537,20 +537,20 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re sortable: !1, tdClass: "text-right text-sm font-medium" } - ]), L = n(() => j.search.trim() !== "" || j.status !== "ACTIVE"), te = n(() => !y.value && C.value === 0 && !L.value), B; + ]), L = n(() => j.search.trim() !== "" || j.status !== "ACTIVE"), V = n(() => !y.value && C.value === 0 && !L.value), H; T(() => j.search, () => { - clearTimeout(B), B = setTimeout(() => re(), Le); - }), T(() => j.status, () => re()), m(() => clearTimeout(B)); - async function ne({ page: e, sort: n }) { + clearTimeout(H), H = setTimeout(() => te(), Ie); + }), T(() => j.status, () => te()), m(() => clearTimeout(H)); + async function ee({ page: e, sort: n }) { let r = { page: e, - limit: Ie, + limit: Fe, ...P(n, l) }; j.status !== "ALL" && (r.status = j.status), j.search.trim() !== "" && (r.search = j.search.trim()), y.value = !0; try { let e = await F(t.client, r); - return C.value = e.meta.total, e.data.some((e) => e.customer_id !== null) && Ce(t.client), { + return C.value = e.meta.total, e.data.some((e) => e.customer_id !== null) && Se(t.client), { data: e.data, pagination: { totalPages: e.meta.last_page, @@ -560,72 +560,72 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re } }; } catch (e) { - return t.notify("error", V(e, u("tasks_projects.projects.load_failed"))), { + return t.notify("error", U(e, u("tasks_projects.projects.load_failed"))), { data: [], pagination: { totalPages: 1, currentPage: 1, totalCount: 0, - limit: Ie + limit: Fe } }; } finally { y.value = !1; } } - function re(e = !1) { + function te(e = !1) { d.value?.refresh(e); } - function ie() { - h.value && ae(), h.value = !h.value; + function ne() { + h.value && re(), h.value = !h.value; } - function ae() { + function re() { j.search = "", j.status = "ACTIVE"; } - function oe() { + function ie() { k.value = null, O.value = !0; } - function se(e) { + function ae(e) { k.value = e, O.value = !0; } - function le(e) { + function se(e) { let n = k.value ? u("tasks_projects.projects.updated", { name: e.name }) : u("tasks_projects.projects.created", { name: e.name }); - O.value = !1, k.value = null, t.notify("success", n), re(); + O.value = !1, k.value = null, t.notify("success", n), te(); } - async function ue(e) { + async function ce(e) { A.value = e.id; try { - e.status === "ARCHIVED" ? (await R(t.client, e.id), t.notify("success", u("tasks_projects.projects.unarchived", { name: e.name }))) : (await ee(t.client, e.id), t.notify("success", u("tasks_projects.projects.archived", { name: e.name }))), re(!0); + e.status === "ARCHIVED" ? (await z(t.client, e.id), t.notify("success", u("tasks_projects.projects.unarchived", { name: e.name }))) : (await R(t.client, e.id), t.notify("success", u("tasks_projects.projects.archived", { name: e.name }))), te(!0); } catch (e) { - t.notify("error", V(e, u("tasks_projects.projects.save_failed"))); + t.notify("error", U(e, u("tasks_projects.projects.save_failed"))); } finally { A.value = null; } } - async function de(e) { + async function le(e) { if (window.confirm(u("tasks_projects.projects.delete_confirm", { name: e.name }))) { A.value = e.id; try { - await z(t.client, e.id), t.notify("success", u("tasks_projects.projects.deleted", { name: e.name })), re(!0); + await B(t.client, e.id), t.notify("success", u("tasks_projects.projects.deleted", { name: e.name })), te(!0); } catch (e) { - t.notify("error", V(e, u("tasks_projects.projects.delete_failed"))); + t.notify("error", U(e, u("tasks_projects.projects.delete_failed"))); } finally { A.value = null; } } } - function fe(e) { + function ue(e) { return e === "ACTIVE" ? "bg-primary-50! text-primary-500!" : "bg-surface-tertiary! text-muted!"; } - function pe(e) { + function fe(e) { return u(e === "ACTIVE" ? "tasks_projects.projects.status.active" : "tasks_projects.projects.status.archived"); } return (t, n) => { - let l = b("BaseBreadcrumbItem"), m = b("BaseBreadcrumb"), _ = b("BaseIcon"), v = b("BaseButton"), y = b("router-link"), C = b("BasePageHeader"), T = b("BaseInput"), P = b("BaseInputGroup"), F = b("BaseSelectInput"), L = b("BaseFilterWrapper"), ee = b("BaseEmptyPlaceholder"), R = b("BaseBadge"), z = b("BaseFormatMoney"), B = b("BaseDropdownItem"), V = b("BaseDropdown"), re = b("BaseTable"), H = b("BasePage"); - return g(), r(H, null, { + let l = b("BaseBreadcrumbItem"), m = b("BaseBreadcrumb"), _ = b("BaseIcon"), v = b("BaseButton"), y = b("router-link"), C = b("BasePageHeader"), T = b("BaseInput"), P = b("BaseInputGroup"), F = b("BaseSelectInput"), L = b("BaseFilterWrapper"), R = b("BaseEmptyPlaceholder"), z = b("BaseBadge"), B = b("BaseFormatMoney"), H = b("BaseDropdownItem"), U = b("BaseDropdown"), te = b("BaseTable"), de = b("BasePage"); + return g(), r(de, null, { default: E(() => [ c(C, { title: S(u)("tasks_projects.projects.title") }, { - actions: E(() => [o("div", De, [ + actions: E(() => [o("div", Ee, [ c(y, { to: "/admin/modules/tasks-projects/board" }, { default: E(() => [c(v, { variant: "white" }, { left: E((e) => [c(_, { @@ -650,7 +650,7 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re }), c(v, { variant: "primary-outline", - onClick: ie + onClick: ne }, { right: E((e) => [h.value ? (g(), r(_, { key: 1, @@ -666,7 +666,7 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re }), c(v, { variant: "primary", - onClick: oe + onClick: ie }, { left: E((e) => [c(_, { name: "PlusIcon", @@ -692,7 +692,7 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re c(L, { show: h.value, class: "mt-3", - onClear: ae + onClear: re }, { default: E(() => [c(P, { label: S(u)("tasks_projects.general.search"), @@ -721,13 +721,13 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re }, 8, ["label"])]), _: 1 }, 8, ["show"]), - D(c(ee, { + D(c(R, { title: S(u)("tasks_projects.projects.empty_title"), description: S(u)("tasks_projects.projects.empty_description") }, { actions: E(() => [c(v, { variant: "primary", - onClick: oe + onClick: ie }, { left: E((e) => [c(_, { name: "PlusIcon", @@ -741,15 +741,15 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re class: "mt-5 mb-4 h-16 w-16 text-subtle" })]), _: 1 - }, 8, ["title", "description"]), [[w, te.value]]), - D(o("div", Oe, [c(re, { + }, 8, ["title", "description"]), [[w, V.value]]), + D(o("div", De, [c(te, { ref_key: "tableRef", ref: d, - data: ne, + data: ee, columns: I.value, class: "mt-3" }, { - "cell-name": E(({ row: e }) => [o("div", ke, [o("span", { + "cell-name": E(({ row: e }) => [o("div", Oe, [o("span", { class: f(["mr-3 inline-block h-2.5 w-2.5 shrink-0 rounded-full", e.data.colour ? "" : "bg-line-default"]), style: p(e.data.colour ? { backgroundColor: e.data.colour } : void 0) }, null, 6), o("span", null, [c(y, { @@ -761,38 +761,38 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re }, { default: E(() => [s(x(e.data.name), 1)]), _: 2 - }, 1032, ["to"]), e.data.identifier ? (g(), a("span", Ae, x(e.data.identifier), 1)) : i("", !0)])])]), - "cell-status": E(({ row: e }) => [c(R, { class: f(["rounded-full", fe(e.data.status)]) }, { - default: E(() => [s(x(pe(e.data.status)), 1)]), + }, 1032, ["to"]), e.data.identifier ? (g(), a("span", ke, x(e.data.identifier), 1)) : i("", !0)])])]), + "cell-status": E(({ row: e }) => [c(z, { class: f(["rounded-full", ue(e.data.status)]) }, { + default: E(() => [s(x(fe(e.data.status)), 1)]), _: 2 }, 1032, ["class"])]), - "cell-customer": E(({ row: e }) => [e.data.customer_id ? (g(), a("span", je, x(S(Se)(e.data.customer_id)), 1)) : (g(), a("span", Me, x(S(u)("tasks_projects.projects.internal")), 1))]), - "cell-default_rate": E(({ row: e }) => [e.data.default_rate === null ? (g(), a("span", Ne, "-")) : (g(), r(z, { + "cell-customer": E(({ row: e }) => [e.data.customer_id ? (g(), a("span", Ae, x(S(xe)(e.data.customer_id)), 1)) : (g(), a("span", je, x(S(u)("tasks_projects.projects.internal")), 1))]), + "cell-default_rate": E(({ row: e }) => [e.data.default_rate === null ? (g(), a("span", Me, "-")) : (g(), r(B, { key: 0, amount: e.data.default_rate }, null, 8, ["amount"]))]), - "cell-due_date": E(({ row: e }) => [e.data.due_date ? (g(), a("span", Pe, x(S(ce)(e.data.due_date)), 1)) : (g(), a("span", Fe, "-"))]), - "cell-actions": E(({ row: e }) => [c(V, { "content-loading": A.value === e.data.id }, { + "cell-due_date": E(({ row: e }) => [e.data.due_date ? (g(), a("span", Ne, x(S(oe)(e.data.due_date)), 1)) : (g(), a("span", Pe, "-"))]), + "cell-actions": E(({ row: e }) => [c(U, { "content-loading": A.value === e.data.id }, { activator: E(() => [c(_, { name: "EllipsisHorizontalIcon", class: "h-5 text-muted" })]), default: E(() => [ - c(B, { onClick: (t) => se(e.data) }, { + c(H, { onClick: (t) => ae(e.data) }, { default: E(() => [c(_, { name: "PencilIcon", class: "mr-3 h-5 w-5 text-subtle group-hover:text-muted" }), s(" " + x(S(u)("tasks_projects.general.edit")), 1)]), _: 1 }, 8, ["onClick"]), - c(B, { onClick: (t) => ue(e.data) }, { + c(H, { onClick: (t) => ce(e.data) }, { default: E(() => [c(_, { name: e.data.status === "ARCHIVED" ? "ArrowPathIcon" : "ArchiveBoxIcon", class: "mr-3 h-5 w-5 text-subtle group-hover:text-muted" }, null, 8, ["name"]), s(" " + x(e.data.status === "ARCHIVED" ? S(u)("tasks_projects.projects.unarchive") : S(u)("tasks_projects.projects.archive")), 1)]), _: 2 }, 1032, ["onClick"]), - c(B, { onClick: (t) => de(e.data) }, { + c(H, { onClick: (t) => le(e.data) }, { default: E(() => [c(_, { name: "TrashIcon", class: "mr-3 h-5 w-5 text-subtle group-hover:text-muted" @@ -803,14 +803,14 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re _: 2 }, 1032, ["content-loading"])]), _: 1 - }, 8, ["columns"])], 512), [[w, !te.value]]), - c(ve, { + }, 8, ["columns"])], 512), [[w, !V.value]]), + c(_e, { show: O.value, client: e.client, notify: e.notify, project: k.value, onClose: n[2] ||= (e) => O.value = !1, - onSaved: le + onSaved: se }, null, 8, [ "show", "client", @@ -822,193 +822,193 @@ var De = { class: "flex items-center justify-end space-x-5" }, Oe = { class: "re }); }; } -}), ze = "/api/v1/tasks-projects", Be = { - timeEntries: `${ze}/time-entries`, - timeEntry: (e) => `${ze}/time-entries/${e}`, - timer: `${ze}/timer`, - timerStart: `${ze}/timer/start`, - timerStop: `${ze}/timer/stop`, - taskStatuses: `${ze}/task-statuses`, - taskStatus: (e) => `${ze}/task-statuses/${e}`, - reorderTaskStatuses: `${ze}/task-statuses/reorder`, - tasks: `${ze}/tasks`, - task: (e) => `${ze}/tasks/${e}`, - members: `${ze}/members`, - settings: `${ze}/settings` -}, Ve = { bootstrap: "/api/v1/bootstrap" }, He = 100, Ue = 5, We = 10; -async function Ge(e, t) { - let { data: n } = await e.get(Be.timeEntries, { params: t }); +}), Re = "/api/v1/tasks-projects", ze = { + timeEntries: `${Re}/time-entries`, + timeEntry: (e) => `${Re}/time-entries/${e}`, + timer: `${Re}/timer`, + timerStart: `${Re}/timer/start`, + timerStop: `${Re}/timer/stop`, + taskStatuses: `${Re}/task-statuses`, + taskStatus: (e) => `${Re}/task-statuses/${e}`, + reorderTaskStatuses: `${Re}/task-statuses/reorder`, + tasks: `${Re}/tasks`, + task: (e) => `${Re}/tasks/${e}`, + members: `${Re}/members`, + settings: `${Re}/settings` +}, Be = { bootstrap: "/api/v1/bootstrap" }, Ve = 100, He = 5, Ue = 10; +async function We(e, t) { + let { data: n } = await e.get(ze.timeEntries, { params: t }); return n; } -async function Ke(e, t) { +async function Ge(e, t) { let n = []; - for (let r = 1; r <= Ue; r += 1) { - let i = await Ge(e, { + for (let r = 1; r <= He; r += 1) { + let i = await We(e, { ...t, page: r, - limit: He + limit: Ve }); if (n.push(...i.data ?? []), !i.meta || r >= i.meta.last_page) break; } return n; } -async function qe(e, t) { - let { data: n } = await e.post(Be.timeEntries, t); +async function Ke(e, t) { + let { data: n } = await e.post(ze.timeEntries, t); return n.data; } -async function Je(e, t, n) { - let { data: r } = await e.put(Be.timeEntry(t), n); +async function qe(e, t, n) { + let { data: r } = await e.put(ze.timeEntry(t), n); return r.data; } -async function Ye(e, t) { - await e.delete(Be.timeEntry(t)); +async function Je(e, t) { + await e.delete(ze.timeEntry(t)); } -async function Xe(e) { - let { data: t } = await e.get(Be.timer); +async function Ye(e) { + let { data: t } = await e.get(ze.timer); return t?.data ?? null; } -async function Ze(e, t) { - let { data: n } = await e.post(Be.timerStart, t); +async function Xe(e, t) { + let { data: n } = await e.post(ze.timerStart, t); return n.data; } -async function Qe(e) { - let { data: t } = await e.post(Be.timerStop); +async function Ze(e) { + let { data: t } = await e.post(ze.timerStop); return t.data; } -async function $e(e) { - await e.delete(Be.timer); +async function Qe(e) { + await e.delete(ze.timer); } -async function et(e) { - let { data: t } = await e.get(Be.taskStatuses); +async function $e(e) { + let { data: t } = await e.get(ze.taskStatuses); return t.data ?? []; } -async function tt(e, t) { - let { data: n } = await e.post(Be.taskStatuses, t); +async function et(e, t) { + let { data: n } = await e.post(ze.taskStatuses, t); return n.data; } -async function nt(e, t, n) { - let { data: r } = await e.put(Be.taskStatus(t), n); +async function tt(e, t, n) { + let { data: r } = await e.put(ze.taskStatus(t), n); return r.data; } -async function rt(e, t) { - await e.delete(Be.taskStatus(t)); +async function nt(e, t) { + await e.delete(ze.taskStatus(t)); } -async function it(e, t) { - let { data: n } = await e.post(Be.reorderTaskStatuses, { ids: t }); +async function rt(e, t) { + let { data: n } = await e.post(ze.reorderTaskStatuses, { ids: t }); return n.data ?? []; } -async function at(e, t, n = We) { +async function it(e, t, n = Ue) { let r = { limit: n }; t.trim() !== "" && (r.search = t.trim()); - let { data: i } = await e.get(Be.tasks, { params: r }); + let { data: i } = await e.get(ze.tasks, { params: r }); return i.data ?? []; } -async function ot(e, t) { - let { data: n } = await e.get(Be.task(t)); +async function at(e, t) { + let { data: n } = await e.get(ze.task(t)); return n.data; } -async function st(e) { - let { data: t } = await e.get(Be.members); +async function ot(e) { + let { data: t } = await e.get(ze.members); return t.data ?? []; } -async function ct(e) { - let { data: t } = await e.get(Be.settings); +async function st(e) { + let { data: t } = await e.get(ze.settings); return t.data; } -async function lt(e) { - let { data: t } = await e.get(Ve.bootstrap), n = t?.current_user?.id; +async function ct(e) { + let { data: t } = await e.get(Be.bootstrap), n = t?.current_user?.id; return typeof n == "number" ? n : null; } //#endregion //#region resources/js/stores/tasks.ts -var ut = _({}), dt = /* @__PURE__ */ new Set(), ft = 5; -function pt(e) { - return e === null ? "" : ut[e] ?? `#${e}`; +var lt = _({}), ut = /* @__PURE__ */ new Set(), dt = 5; +function ft(e) { + return e === null ? "" : lt[e] ?? `#${e}`; } -function mt(e) { - e && typeof e.id == "number" && typeof e.name == "string" && (ut[e.id] = e.name); +function pt(e) { + e && typeof e.id == "number" && typeof e.name == "string" && (lt[e.id] = e.name); } -async function ht(e, t) { - let n = [...new Set(t)].filter((e) => typeof e == "number" && ut[e] === void 0 && !dt.has(e)); - for (let e of n) dt.add(e); - for (let t = 0; t < n.length; t += ft) await Promise.all(n.slice(t, t + ft).map(async (t) => { +async function mt(e, t) { + let n = [...new Set(t)].filter((e) => typeof e == "number" && lt[e] === void 0 && !ut.has(e)); + for (let e of n) ut.add(e); + for (let t = 0; t < n.length; t += dt) await Promise.all(n.slice(t, t + dt).map(async (t) => { try { - mt(await ot(e, t)); + pt(await at(e, t)); } catch {} finally { - dt.delete(t); + ut.delete(t); } })); } -function gt() { - for (let e of Object.keys(ut)) delete ut[Number(e)]; - dt.clear(); +function ht() { + for (let e of Object.keys(lt)) delete lt[Number(e)]; + ut.clear(); } //#endregion //#region resources/js/support/http.ts -function _t(e) { +function gt(e) { if (typeof e != "object" || !e) return null; let t = e.response?.status; return typeof t == "number" ? t : null; } -function vt(e) { - return _t(e) === 409; +function _t(e) { + return gt(e) === 409; } -function yt(e) { - return _t(e) === 403; +function vt(e) { + return gt(e) === 403; } //#endregion //#region resources/js/support/time.ts -var bt = 60, xt = 60, St = 7; -function Ct(e) { - let t = Number.isFinite(e) && e > 0 ? Math.floor(e) : 0, n = Math.floor(t / 3600), r = Math.floor(t % 3600 / xt), i = t % xt; - return `${n}:${Bt(r)}:${Bt(i)}`; +var yt = 60, bt = 60, xt = 7; +function St(e) { + let t = Number.isFinite(e) && e > 0 ? Math.floor(e) : 0, n = Math.floor(t / 3600), r = Math.floor(t % 3600 / bt), i = t % bt; + return `${n}:${zt(r)}:${zt(i)}`; } -function wt(e) { +function Ct(e) { let t = e !== null && Number.isFinite(e) && e > 0 ? Math.round(e) : 0; - return `${Math.floor(t / bt)}:${Bt(t % bt)}`; + return `${Math.floor(t / yt)}:${zt(t % yt)}`; } -function Tt(e) { +function wt(e) { let t = e.trim(); if (t === "") return null; let n = /^(\d+):([0-5]?\d)$/.exec(t); - if (n) return Number(n[1]) * bt + Number(n[2]); + if (n) return Number(n[1]) * yt + Number(n[2]); if (!/^\d+([.,]\d+)?$/.test(t)) return null; let r = Number(t.replace(",", ".")); - return Number.isNaN(r) ? null : Math.round(r * bt); + return Number.isNaN(r) ? null : Math.round(r * yt); } -function Et(e) { - let t = Lt(e); - return t === null ? "" : Nt(t); +function Tt(e) { + let t = It(e); + return t === null ? "" : Mt(t); } -function Dt(e) { - let t = Lt(e); - return t === null ? "" : `${Bt(t.getHours())}:${Bt(t.getMinutes())}`; +function Et(e) { + let t = It(e); + return t === null ? "" : `${zt(t.getHours())}:${zt(t.getMinutes())}`; } -function Ot(e, t = "09:00") { - let n = Rt(e), r = /^(\d{1,2}):([0-5]\d)$/.exec(t.trim()); +function Dt(e, t = "09:00") { + let n = Lt(e), r = /^(\d{1,2}):([0-5]\d)$/.exec(t.trim()); if (n === null || r === null) return null; let i = Number(r[1]); return i > 23 ? null : (n.setHours(i, Number(r[2]), 0, 0), n.toISOString()); } -function kt(e, t) { +function Ot(e, t) { let n = new Date(e); - return n.setTime(n.getTime() + t * xt * 1e3), n.toISOString(); + return n.setTime(n.getTime() + t * bt * 1e3), n.toISOString(); } -function At(e, t) { - let n = Number.isInteger(t) && t >= 0 && t <= 6 ? t : 1, r = zt(e), i = (r.getDay() - n + St) % St; +function kt(e, t) { + let n = Number.isInteger(t) && t >= 0 && t <= 6 ? t : 1, r = Rt(e), i = (r.getDay() - n + xt) % xt; return r.setDate(r.getDate() - i), r; } -function jt(e) { - return Array.from({ length: St }, (t, n) => Mt(e, n)); +function At(e) { + return Array.from({ length: xt }, (t, n) => jt(e, n)); } -function Mt(e, t) { - let n = zt(e); +function jt(e, t) { + let n = Rt(e); return n.setDate(n.getDate() + t), n; } -function Nt(e) { - return `${e.getFullYear()}-${Bt(e.getMonth() + 1)}-${Bt(e.getDate())}`; +function Mt(e) { + return `${e.getFullYear()}-${zt(e.getMonth() + 1)}-${zt(e.getDate())}`; } -function Pt(e) { +function Nt(e) { return { weekday: e.toLocaleDateString(void 0, { weekday: "short" }), day: e.toLocaleDateString(void 0, { @@ -1017,140 +1017,140 @@ function Pt(e) { }) }; } -function Ft(e) { - return Nt(e) === Nt(/* @__PURE__ */ new Date()); +function Pt(e) { + return Mt(e) === Mt(/* @__PURE__ */ new Date()); } -function It(e) { - let t = Lt(e); +function Ft(e) { + let t = It(e); return t === null ? 0 : Math.max(0, Math.floor((Date.now() - t.getTime()) / 1e3)); } -function Lt(e) { +function It(e) { if (!e) return null; let t = new Date(e); return Number.isNaN(t.getTime()) ? null : t; } -function Rt(e) { +function Lt(e) { let t = /^(\d{4})-(\d{2})-(\d{2})/.exec(e.trim()); if (t === null) return null; let n = new Date(Number(t[1]), Number(t[2]) - 1, Number(t[3]), 0, 0, 0, 0); return Number.isNaN(n.getTime()) ? null : n; } -function zt(e) { +function Rt(e) { let t = new Date(e.getTime()); return t.setHours(0, 0, 0, 0), t; } -function Bt(e) { +function zt(e) { return String(e).padStart(2, "0"); } //#endregion //#region resources/js/stores/timer.ts -var U = _({ +var W = _({ running: null, elapsedSeconds: 0, busy: !1 -}), Vt; +}), Bt; +function Vt() { + W.elapsedSeconds = W.running === null ? 0 : Ft(W.running.started_at); +} function Ht() { - U.elapsedSeconds = U.running === null ? 0 : It(U.running.started_at); + Vt(), Bt === void 0 && (Bt = setInterval(Vt, 1e3)); } function Ut() { - Ht(), Vt === void 0 && (Vt = setInterval(Ht, 1e3)); -} -function Wt() { - Vt !== void 0 && (clearInterval(Vt), Vt = void 0), U.elapsedSeconds = 0; + Bt !== void 0 && (clearInterval(Bt), Bt = void 0), W.elapsedSeconds = 0; } -function Gt(e, t) { - if (U.running = e && typeof e.id == "number" ? e : null, U.running === null) { - Wt(); +function Wt(e, t) { + if (W.running = e && typeof e.id == "number" ? e : null, W.running === null) { + Ut(); return; } - Ut(), t && typeof U.running.task_id == "number" && ht(t, [U.running.task_id]); + Ht(), t && typeof W.running.task_id == "number" && mt(t, [W.running.task_id]); } -function Kt(e, t, n) { - e?.notify("error", V(t, e.t(n))); +function Gt(e, t, n) { + e?.notify("error", U(t, e.t(n))); } -var W = { +var G = { get running() { - return U.running; + return W.running; }, get elapsedSeconds() { - return U.elapsedSeconds; + return W.elapsedSeconds; }, get busy() { - return U.busy; + return W.busy; }, async refresh(e) { try { - Gt(await Xe(e), e); + Wt(await Ye(e), e); } catch { - Gt(null); + Wt(null); } }, async start(e, t, n = null, r) { - if (U.busy) return null; - U.busy = !0; + if (W.busy) return null; + W.busy = !0; try { - let r = await Ze(e, { + let r = await Xe(e, { task_id: t, description: n }); - return Gt(r, e), r; + return Wt(r, e), r; } catch (t) { - return vt(t) ? (r?.notify("warning", r.t("tasks_projects.timer.already_running")), await this.refresh(e)) : Kt(r, t, "tasks_projects.timer.start_failed"), null; + return _t(t) ? (r?.notify("warning", r.t("tasks_projects.timer.already_running")), await this.refresh(e)) : Gt(r, t, "tasks_projects.timer.start_failed"), null; } finally { - U.busy = !1; + W.busy = !1; } }, async stop(e, t) { - if (U.busy || U.running === null) return null; - U.busy = !0; + if (W.busy || W.running === null) return null; + W.busy = !0; try { - let t = await Qe(e); - return Gt(null), t; + let t = await Ze(e); + return Wt(null), t; } catch (n) { - return Kt(t, n, "tasks_projects.timer.stop_failed"), await this.refresh(e), null; + return Gt(t, n, "tasks_projects.timer.stop_failed"), await this.refresh(e), null; } finally { - U.busy = !1; + W.busy = !1; } }, async discard(e, t) { - if (U.busy || U.running === null) return !1; - U.busy = !0; + if (W.busy || W.running === null) return !1; + W.busy = !0; try { - return await $e(e), Gt(null), !0; + return await Qe(e), Wt(null), !0; } catch (n) { - return Kt(t, n, "tasks_projects.timer.discard_failed"), await this.refresh(e), !1; + return Gt(t, n, "tasks_projects.timer.discard_failed"), await this.refresh(e), !1; } finally { - U.busy = !1; + W.busy = !1; } }, reset() { - U.busy = !1, Gt(null); + W.busy = !1, Wt(null); } -}, qt = { +}, Kt = { key: 0, class: "fixed right-6 bottom-20 z-40 flex flex-col items-end gap-3" -}, Jt = ["aria-label"], Yt = { class: "flex items-center justify-between border-b border-line-default px-4 py-3" }, Xt = { class: "text-sm font-semibold text-heading" }, Zt = ["aria-label"], Qt = { +}, qt = ["aria-label"], Jt = { class: "flex items-center justify-between border-b border-line-default px-4 py-3" }, Yt = { class: "text-sm font-semibold text-heading" }, Xt = ["aria-label"], Zt = { key: 0, class: "space-y-4 px-4 py-4" -}, $t = { class: "truncate text-sm font-medium text-heading" }, en = { class: "mt-1 text-2xl font-semibold tabular-nums text-primary-500" }, tn = { +}, Qt = { class: "truncate text-sm font-medium text-heading" }, $t = { class: "mt-1 text-2xl font-semibold tabular-nums text-primary-500" }, en = { key: 0, class: "mt-1 text-xs text-muted" -}, nn = { class: "flex items-center gap-2" }, rn = { +}, tn = { class: "flex items-center gap-2" }, nn = { key: 1, class: "space-y-3 px-4 py-4" -}, an = { class: "block" }, on = { class: "sr-only" }, sn = ["placeholder"], cn = { +}, rn = { class: "block" }, an = { class: "sr-only" }, on = ["placeholder"], sn = { key: 0, class: "text-xs text-muted" -}, ln = { +}, cn = { key: 1, class: "max-h-48 space-y-1 overflow-y-auto" -}, un = ["onClick"], dn = { +}, ln = ["onClick"], un = { key: 2, class: "text-xs text-muted" -}, fn = ["placeholder", "aria-label"], pn = { class: "flex items-center justify-between" }, mn = ["title", "aria-label"], hn = { +}, dn = ["placeholder", "aria-label"], fn = { class: "flex items-center justify-between" }, pn = ["title", "aria-label"], mn = { key: 0, class: "tabular-nums" -}, gn = 300, _n = /* @__PURE__ */ l({ +}, hn = 300, gn = /* @__PURE__ */ l({ __name: "QuickStartOverlay", props: { client: { type: [Function, Object] }, @@ -1159,91 +1159,91 @@ var W = { }, emits: ["open-timesheet"], setup(l, { emit: u }) { - let d = l, p = u, h = H(), _ = v(!1), w = v(""), k = v([]), A = v(!1), j = v(null), M = v(""), N, P = n(() => ({ + let d = l, p = u, h = de(), _ = v(!1), w = v(""), k = v([]), A = v(!1), j = v(null), M = v(""), N, P = n(() => ({ notify: d.notify, t: h - })), F = n(() => pt(W.running?.task_id ?? null)), I = n(() => Ct(W.elapsedSeconds)); + })), F = n(() => ft(G.running?.task_id ?? null)), I = n(() => St(G.elapsedSeconds)); T(() => d.enabled, (e) => { - e || R(); + e || z(); }), T(_, (e) => { - e && W.running === null && L(); + e && G.running === null && L(); }), T(w, () => { - clearTimeout(N), N = setTimeout(() => void L(), gn); + clearTimeout(N), N = setTimeout(() => void L(), hn); }), m(() => clearTimeout(N)); async function L() { A.value = !0; try { - let e = await at(d.client, w.value); - k.value = e, e.forEach(mt); + let e = await it(d.client, w.value); + k.value = e, e.forEach(pt); } catch (e) { - k.value = [], d.notify("error", V(e, h("tasks_projects.time.tasks_failed"))); + k.value = [], d.notify("error", U(e, h("tasks_projects.time.tasks_failed"))); } finally { A.value = !1; } } - function ee(e) { - j.value = e, mt(e); + function R(e) { + j.value = e, pt(e); } - function R() { + function z() { _.value = !1, w.value = "", k.value = [], j.value = null, M.value = ""; } - async function z() { + async function B() { let e = j.value; - e !== null && await W.start(d.client, e.id, M.value.trim() || null, P.value) !== null && (d.notify("success", h("tasks_projects.timer.started", { name: e.name })), R()); + e !== null && await G.start(d.client, e.id, M.value.trim() || null, P.value) !== null && (d.notify("success", h("tasks_projects.timer.started", { name: e.name })), z()); } - async function te() { - let e = F.value, t = await W.stop(d.client, P.value); + async function V() { + let e = F.value, t = await G.stop(d.client, P.value); t !== null && (d.notify("success", h("tasks_projects.timer.stopped", { name: e, - duration: wt(t.duration_minutes) - })), R()); + duration: Ct(t.duration_minutes) + })), z()); } - async function B() { - window.confirm(h("tasks_projects.timer.discard_confirm")) && await W.discard(d.client, P.value) && (d.notify("success", h("tasks_projects.timer.discarded")), R()); + async function H() { + window.confirm(h("tasks_projects.timer.discard_confirm")) && await G.discard(d.client, P.value) && (d.notify("success", h("tasks_projects.timer.discarded")), z()); } return (n, u) => { let d = b("BaseIcon"), m = b("BaseButton"); - return g(), r(t, { to: "body" }, [l.enabled ? (g(), a("div", qt, [_.value ? (g(), a("section", { + return g(), r(t, { to: "body" }, [l.enabled ? (g(), a("div", Kt, [_.value ? (g(), a("section", { key: 0, class: "w-80 max-w-[calc(100vw-3rem)] rounded-xl border border-line-default bg-surface shadow-2xl", "aria-label": S(h)("tasks_projects.timer.panel_title"), - onKeydown: O(R, ["esc"]) - }, [o("header", Yt, [o("h2", Xt, x(S(h)("tasks_projects.timer.panel_title")), 1), o("button", { + onKeydown: O(z, ["esc"]) + }, [o("header", Jt, [o("h2", Yt, x(S(h)("tasks_projects.timer.panel_title")), 1), o("button", { type: "button", class: "rounded p-1 text-subtle hover:bg-hover hover:text-heading", "aria-label": S(h)("tasks_projects.timer.close"), - onClick: R + onClick: z }, [c(d, { name: "XMarkIcon", class: "h-5 w-5" - })], 8, Zt)]), S(W).running === null ? (g(), a("div", rn, [ - o("label", an, [o("span", on, x(S(h)("tasks_projects.timer.search_tasks")), 1), D(o("input", { + })], 8, Xt)]), S(G).running === null ? (g(), a("div", nn, [ + o("label", rn, [o("span", an, x(S(h)("tasks_projects.timer.search_tasks")), 1), D(o("input", { "onUpdate:modelValue": u[0] ||= (e) => w.value = e, type: "search", autocomplete: "off", class: "w-full rounded-md border border-line-default bg-surface px-3 py-2 text-sm text-body outline-hidden focus:border-primary-400 focus:ring-1 focus:ring-primary-400", placeholder: S(h)("tasks_projects.timer.search_tasks") - }, null, 8, sn), [[C, w.value]])]), - A.value ? (g(), a("p", cn, x(S(h)("tasks_projects.general.search")), 1)) : k.value.length > 0 ? (g(), a("ul", ln, [(g(!0), a(e, null, y(k.value, (e) => (g(), a("li", { key: e.id }, [o("button", { + }, null, 8, on), [[C, w.value]])]), + A.value ? (g(), a("p", sn, x(S(h)("tasks_projects.general.search")), 1)) : k.value.length > 0 ? (g(), a("ul", cn, [(g(!0), a(e, null, y(k.value, (e) => (g(), a("li", { key: e.id }, [o("button", { type: "button", class: f(["w-full truncate rounded-md px-2 py-2 text-left text-sm hover:bg-hover", j.value?.id === e.id ? "bg-hover-strong font-medium text-heading" : "text-body"]), - onClick: (t) => ee(e) - }, x(e.name), 11, un)]))), 128))])) : (g(), a("p", dn, x(S(h)("tasks_projects.timer.no_tasks")), 1)), + onClick: (t) => R(e) + }, x(e.name), 11, ln)]))), 128))])) : (g(), a("p", un, x(S(h)("tasks_projects.timer.no_tasks")), 1)), D(o("input", { "onUpdate:modelValue": u[1] ||= (e) => M.value = e, type: "text", class: "w-full rounded-md border border-line-default bg-surface px-3 py-2 text-sm text-body outline-hidden focus:border-primary-400 focus:ring-1 focus:ring-primary-400", placeholder: S(h)("tasks_projects.timer.description_placeholder"), "aria-label": S(h)("tasks_projects.time.fields.description") - }, null, 8, fn), [[C, M.value]]), - o("div", pn, [o("button", { + }, null, 8, dn), [[C, M.value]]), + o("div", fn, [o("button", { type: "button", class: "text-xs text-primary-500 hover:underline", onClick: u[2] ||= (e) => p("open-timesheet") }, x(S(h)("tasks_projects.timer.open_timesheet")), 1), c(m, { variant: "primary", - disabled: j.value === null || S(W).busy, - onClick: z + disabled: j.value === null || S(G).busy, + onClick: B }, { left: E((e) => [c(d, { name: "PlayIcon", @@ -1252,14 +1252,14 @@ var W = { default: E(() => [s(" " + x(S(h)("tasks_projects.timer.start")), 1)]), _: 1 }, 8, ["disabled"])]) - ])) : (g(), a("div", Qt, [o("div", null, [ - o("p", $t, x(F.value), 1), - o("p", en, x(I.value), 1), - S(W).running.description ? (g(), a("p", tn, x(S(W).running.description), 1)) : i("", !0) - ]), o("div", nn, [c(m, { + ])) : (g(), a("div", Zt, [o("div", null, [ + o("p", Qt, x(F.value), 1), + o("p", $t, x(I.value), 1), + S(G).running.description ? (g(), a("p", en, x(S(G).running.description), 1)) : i("", !0) + ]), o("div", tn, [c(m, { variant: "primary", - disabled: S(W).busy, - onClick: te + disabled: S(G).busy, + onClick: V }, { left: E((e) => [c(d, { name: "StopIcon", @@ -1269,31 +1269,31 @@ var W = { _: 1 }, 8, ["disabled"]), c(m, { variant: "primary-outline", - disabled: S(W).busy, - onClick: B + disabled: S(G).busy, + onClick: H }, { default: E(() => [s(x(S(h)("tasks_projects.timer.discard")), 1)]), _: 1 - }, 8, ["disabled"])])]))], 40, Jt)) : i("", !0), o("button", { + }, 8, ["disabled"])])]))], 40, qt)) : i("", !0), o("button", { type: "button", class: "flex items-center gap-2 rounded-full bg-btn-primary px-4 py-3 text-sm font-medium text-white shadow-lg hover:bg-btn-primary-hover", title: S(h)("tasks_projects.timer.quick_start"), "aria-label": S(h)("tasks_projects.timer.quick_start"), onClick: u[3] ||= (e) => _.value = !_.value }, [c(d, { - name: S(W).running === null ? "ClockIcon" : "StopIcon", + name: S(G).running === null ? "ClockIcon" : "StopIcon", class: "h-5 w-5 text-white" - }, null, 8, ["name"]), S(W).running === null ? i("", !0) : (g(), a("span", hn, x(I.value), 1))], 8, mn)])) : i("", !0)]); + }, null, 8, ["name"]), S(G).running === null ? i("", !0) : (g(), a("span", mn, x(I.value), 1))], 8, pn)])) : i("", !0)]); }; } -}), vn = { +}), _n = { key: 0, class: "relative float-left m-0 ml-2" -}, yn = ["title"], bn = ["aria-label"], xn = { class: "font-medium tabular-nums" }, Sn = [ +}, vn = ["title"], yn = ["aria-label"], bn = { class: "font-medium tabular-nums" }, xn = [ "disabled", "title", "aria-label" -], Cn = /* @__PURE__ */ l({ +], Sn = /* @__PURE__ */ l({ __name: "TimerChip", props: { client: { type: [Function, Object] }, @@ -1301,20 +1301,20 @@ var W = { }, emits: ["open"], setup(e, { emit: t }) { - let r = e, s = t, l = H(), u = n(() => pt(W.running?.task_id ?? null)), d = n(() => Ct(W.elapsedSeconds)); + let r = e, s = t, l = de(), u = n(() => ft(G.running?.task_id ?? null)), d = n(() => St(G.elapsedSeconds)); async function f() { - let e = u.value, t = await W.stop(r.client, { + let e = u.value, t = await G.stop(r.client, { notify: r.notify, t: l }); t !== null && r.notify("success", l("tasks_projects.timer.stopped", { name: e, - duration: wt(t.duration_minutes) + duration: Ct(t.duration_minutes) })); } return (e, t) => { let n = b("BaseIcon"); - return S(W).running === null ? i("", !0) : (g(), a("li", vn, [o("div", { + return S(G).running === null ? i("", !0) : (g(), a("li", _n, [o("div", { class: "flex h-8 items-center gap-2 rounded-lg bg-white/20 px-2 text-sm text-white md:h-9 md:px-3", title: S(l)("tasks_projects.timer.running") }, [ @@ -1324,23 +1324,23 @@ var W = { class: "hidden max-w-32 truncate hover:underline lg:block", "aria-label": S(l)("tasks_projects.timer.open_timesheet"), onClick: t[0] ||= (e) => s("open") - }, x(u.value), 9, bn), - o("span", xn, x(d.value), 1), + }, x(u.value), 9, yn), + o("span", bn, x(d.value), 1), o("button", { type: "button", class: "rounded p-1 hover:bg-white/20 disabled:opacity-50", - disabled: S(W).busy, + disabled: S(G).busy, title: S(l)("tasks_projects.timer.stop"), "aria-label": S(l)("tasks_projects.timer.stop"), onClick: f }, [c(n, { name: "StopIcon", class: "h-4 w-4 text-white" - })], 8, Sn) - ], 8, yn)])); + })], 8, xn) + ], 8, vn)])); }; } -}), wn = { en: { tasks_projects: { +}), Cn = { en: { tasks_projects: { time: { title: "Time", my_time: "My time", @@ -1465,10 +1465,10 @@ var W = { reorder_failed: "Unable to save the new order.", forbidden: "Your role does not allow managing the board columns." } -} } }, Tn = { class: "relative table-container" }, En = { class: "block max-w-64 truncate" }, Dn = { class: "tabular-nums" }, On = { +} } }, wn = { class: "relative table-container" }, Tn = { class: "block max-w-64 truncate" }, En = { class: "tabular-nums" }, Dn = { key: 1, class: "text-subtle" -}, kn = /* @__PURE__ */ l({ +}, On = /* @__PURE__ */ l({ __name: "AllTimeTable", props: { client: { type: [Function, Object] }, @@ -1479,7 +1479,7 @@ var W = { }, emits: ["edit", "delete"], setup(e, { emit: t }) { - let l = e, u = t, d = H(), p = v(null), m = _({ + let l = e, u = t, d = de(), p = v(null), m = _({ memberId: null, projectId: null, from: "", @@ -1580,10 +1580,10 @@ var W = { m.memberId = null, m.projectId = null, m.from = "", m.to = "", m.billing = "ALL"; } function N(e) { - m.from = e ? le(e) : ""; + m.from = e ? se(e) : ""; } function P(e) { - m.to = e ? le(e) : ""; + m.to = e ? se(e) : ""; } function F(e) { let t = l.members.find((t) => t.id === e); @@ -1596,13 +1596,13 @@ var W = { }; m.memberId !== null && (t.user_id = m.memberId), m.projectId !== null && (t.project_id = m.projectId), m.from !== "" && (t.from = m.from), m.to !== "" && (t.to = m.to), m.billing !== "ALL" && (t.billed = m.billing === "BILLED"); try { - let e = await Ge(l.client, t), n = e.data ?? []; - return ht(l.client, n.map((e) => e.task_id).filter((e) => typeof e == "number")), { + let e = await We(l.client, t), n = e.data ?? []; + return mt(l.client, n.map((e) => e.task_id).filter((e) => typeof e == "number")), { data: n, pagination: L(e.meta, n.length) }; } catch (e) { - return l.notify("error", V(e, d("tasks_projects.time.load_failed"))), { + return l.notify("error", U(e, d("tasks_projects.time.load_failed"))), { data: [], pagination: L(null, 0) }; @@ -1618,7 +1618,7 @@ var W = { }; } return (e, t) => { - let n = b("BaseSelectInput"), l = b("BaseInputGroup"), _ = b("BaseDatePicker"), v = b("BaseFilterWrapper"), T = b("BaseBadge"), A = b("BaseFormatMoney"), j = b("BaseIcon"), L = b("BaseDropdownItem"), ee = b("BaseDropdown"), R = b("BaseTable"); + let n = b("BaseSelectInput"), l = b("BaseInputGroup"), _ = b("BaseDatePicker"), v = b("BaseFilterWrapper"), T = b("BaseBadge"), A = b("BaseFormatMoney"), j = b("BaseIcon"), L = b("BaseDropdownItem"), R = b("BaseDropdown"), z = b("BaseTable"); return g(), a("section", null, [c(v, { show: "", "row-on-xl": "", @@ -1684,18 +1684,18 @@ var W = { }, 8, ["label"]) ]), _: 1 - }), o("div", Tn, [c(R, { + }), o("div", wn, [c(z, { ref_key: "tableRef", ref: p, data: I, columns: k.value, class: "mt-3" }, { - "cell-date": E(({ row: e }) => [s(x(S(ce)(S(Et)(e.data.started_at))), 1)]), + "cell-date": E(({ row: e }) => [s(x(S(oe)(S(Tt)(e.data.started_at))), 1)]), "cell-member": E(({ row: e }) => [s(x(F(e.data.user_id)), 1)]), - "cell-task": E(({ row: e }) => [s(x(S(pt)(e.data.task_id)), 1)]), - "cell-description": E(({ row: e }) => [o("span", En, x(e.data.description || "-"), 1)]), - "cell-duration": E(({ row: e }) => [o("span", Dn, x(S(wt)(e.data.duration_minutes)), 1)]), + "cell-task": E(({ row: e }) => [s(x(S(ft)(e.data.task_id)), 1)]), + "cell-description": E(({ row: e }) => [o("span", Tn, x(e.data.description || "-"), 1)]), + "cell-duration": E(({ row: e }) => [o("span", En, x(S(Ct)(e.data.duration_minutes)), 1)]), "cell-billable": E(({ row: e }) => [c(T, { class: f(["rounded-full", e.data.billable ? "bg-primary-50! text-primary-500!" : "bg-surface-tertiary! text-muted!"]) }, { default: E(() => [s(x(e.data.billable ? S(d)("tasks_projects.time.billable") : S(d)("tasks_projects.time.non_billable")), 1)]), _: 2 @@ -1703,8 +1703,8 @@ var W = { "cell-amount": E(({ row: e }) => [e.data.billable ? (g(), r(A, { key: 0, amount: e.data.amount - }, null, 8, ["amount"])) : (g(), a("span", On, "-"))]), - "cell-actions": E(({ row: e }) => [c(ee, null, { + }, null, 8, ["amount"])) : (g(), a("span", Dn, "-"))]), + "cell-actions": E(({ row: e }) => [c(R, null, { activator: E(() => [c(j, { name: "EllipsisHorizontalIcon", class: "h-5 text-muted" @@ -1731,13 +1731,13 @@ var W = { }, 8, ["columns"])])]); }; } -}), An = { class: "flex w-full items-center justify-between" }, jn = { class: "space-y-5 px-6 py-6" }, Mn = { +}), kn = { class: "flex w-full items-center justify-between" }, An = { class: "space-y-5 px-6 py-6" }, jn = { key: 0, class: "rounded-md bg-alert-warning-bg px-3 py-2 text-sm text-alert-warning-text" -}, Nn = { class: "inline-flex overflow-hidden rounded-md border border-line-default" }, Pn = ["disabled", "onClick"], Fn = { +}, Mn = { class: "inline-flex overflow-hidden rounded-md border border-line-default" }, Nn = ["disabled", "onClick"], Pn = { key: 1, class: "text-sm text-muted" -}, In = { class: "flex items-center justify-between border-t border-line-default px-6 py-4" }, Ln = { key: 1 }, Rn = { class: "flex space-x-3" }, zn = "09:00", Bn = /* @__PURE__ */ l({ +}, Fn = { class: "flex items-center justify-between border-t border-line-default px-6 py-4" }, In = { key: 1 }, Ln = { class: "flex space-x-3" }, Rn = "09:00", zn = /* @__PURE__ */ l({ __name: "TimeEntryModal", props: { show: { type: Boolean }, @@ -1752,11 +1752,11 @@ var W = { "deleted" ], setup(t, { emit: l }) { - let u = t, d = l, p = ["duration", "range"], m = H(), h = _({ + let u = t, d = l, p = ["duration", "range"], m = de(), h = _({ date: "", mode: "duration", duration: "", - start: zn, + start: Rn, end: "", description: "", billable: !0 @@ -1766,7 +1766,7 @@ var W = { }, { immediate: !0 }); function N() { let e = u.entry; - w.value = {}, C.value = null, h.date = e ? Et(e.started_at) : u.defaultDate ?? Nt(/* @__PURE__ */ new Date()), h.duration = e ? wt(e.duration_minutes) : "", h.start = e?.started_at ? Dt(e.started_at) : zn, h.end = e?.ended_at ? Dt(e.ended_at) : "", h.description = e?.description ?? "", h.billable = !e || e.billable, h.mode = e !== null && P(e) ? "range" : "duration", h.date === "" && (h.date = u.defaultDate ?? Nt(/* @__PURE__ */ new Date())), e !== null && F(e.task_id); + w.value = {}, C.value = null, h.date = e ? Tt(e.started_at) : u.defaultDate ?? Mt(/* @__PURE__ */ new Date()), h.duration = e ? Ct(e.duration_minutes) : "", h.start = e?.started_at ? Et(e.started_at) : Rn, h.end = e?.ended_at ? Et(e.ended_at) : "", h.description = e?.description ?? "", h.billable = !e || e.billable, h.mode = e !== null && P(e) ? "range" : "duration", h.date === "" && (h.date = u.defaultDate ?? Mt(/* @__PURE__ */ new Date())), e !== null && F(e.task_id); } function P(e) { if (!e.started_at || !e.ended_at) return !1; @@ -1775,32 +1775,32 @@ var W = { } async function F(e) { try { - let t = await ot(u.client, e); - C.value = t, mt(t); + let t = await at(u.client, e); + C.value = t, pt(t); } catch {} } async function I(e) { try { - let t = await at(u.client, e ?? ""); - return t.forEach(mt), t; + let t = await it(u.client, e ?? ""); + return t.forEach(pt), t; } catch (e) { - return u.notify("error", V(e, m("tasks_projects.time.tasks_failed"))), []; + return u.notify("error", U(e, m("tasks_projects.time.tasks_failed"))), []; } } function L(e) { - h.date = e ? le(e) : ""; + h.date = e ? se(e) : ""; } - function ee(e) { + function R(e) { C.value = e, e !== null && u.entry === null && (h.billable = e.billable !== !1); } - function R() { + function z() { let e = {}, t = C.value; (t === null || typeof t.id != "number") && (e.task_id = m("tasks_projects.time.task_required")), h.date === "" && (e.date = m("tasks_projects.time.date_required")); - let n = Ot(h.date, h.mode === "range" ? h.start : zn); + let n = Dt(h.date, h.mode === "range" ? h.start : Rn); n === null && (e.started_at = m("tasks_projects.time.range_invalid")); - let r = h.mode === "duration" ? Tt(h.duration) : null; + let r = h.mode === "duration" ? wt(h.duration) : null; h.mode === "duration" && r === null && (e.duration_minutes = m("tasks_projects.time.duration_invalid")); - let i = h.mode === "range" ? Ot(h.date, h.end) : null; + let i = h.mode === "range" ? Dt(h.date, h.end) : null; if (h.mode === "range" && (i === null || n === null || i <= n) && (e.ended_at = m("tasks_projects.time.range_invalid")), w.value = e, Object.keys(e).length > 0 || t === null || n === null) return null; let a = { task_id: t.id, @@ -1808,49 +1808,49 @@ var W = { description: h.description.trim() || null, billable: h.billable }; - return h.mode === "duration" && r !== null ? (a.duration_minutes = r, a.ended_at = kt(n, r)) : a.ended_at = i, a; + return h.mode === "duration" && r !== null ? (a.duration_minutes = r, a.ended_at = Ot(n, r)) : a.ended_at = i, a; } - async function z() { + async function B() { if (D.value || j.value) return; - let e = R(); + let e = z(); if (e !== null) { D.value = !0; try { - let t = u.entry, n = t ? await Je(u.client, t.id, e) : await qe(u.client, e); + let t = u.entry, n = t ? await qe(u.client, t.id, e) : await Ke(u.client, e); d("saved", n); } catch (e) { - w.value = re(e), u.notify("error", V(e, m("tasks_projects.time.save_failed"))); + w.value = te(e), u.notify("error", U(e, m("tasks_projects.time.save_failed"))); } finally { D.value = !1; } } } - async function te() { + async function V() { let e = u.entry; if (!(e === null || O.value || j.value) && window.confirm(m("tasks_projects.time.delete_confirm"))) { O.value = !0; try { - await Ye(u.client, e.id), d("deleted", e); + await Je(u.client, e.id), d("deleted", e); } catch (e) { - u.notify("error", V(e, m("tasks_projects.time.delete_failed"))); + u.notify("error", U(e, m("tasks_projects.time.delete_failed"))); } finally { O.value = !1; } } } return (n, l) => { - let u = b("BaseIcon"), _ = b("BaseMultiselect"), v = b("BaseInputGroup"), T = b("BaseDatePicker"), N = b("BaseInputGrid"), P = b("BaseInput"), F = b("BaseTextarea"), R = b("BaseSwitch"), B = b("BaseButton"), ne = b("BaseModal"); - return g(), r(ne, { + let u = b("BaseIcon"), _ = b("BaseMultiselect"), v = b("BaseInputGroup"), T = b("BaseDatePicker"), N = b("BaseInputGrid"), P = b("BaseInput"), F = b("BaseTextarea"), z = b("BaseSwitch"), H = b("BaseButton"), ee = b("BaseModal"); + return g(), r(ee, { show: t.show, onClose: l[8] ||= (e) => d("close") }, { - header: E(() => [o("div", An, [o("span", null, x(M.value), 1), c(u, { + header: E(() => [o("div", kn, [o("span", null, x(M.value), 1), c(u, { name: "XMarkIcon", class: "h-6 w-6 cursor-pointer text-subtle hover:text-body", onClick: l[0] ||= (e) => d("close") })])]), - default: E(() => [o("form", { onSubmit: k(z, ["prevent"]) }, [o("div", jn, [ - j.value ? (g(), a("p", Mn, x(S(m)("tasks_projects.time.stamped_notice")), 1)) : i("", !0), + default: E(() => [o("form", { onSubmit: k(B, ["prevent"]) }, [o("div", An, [ + j.value ? (g(), a("p", jn, x(S(m)("tasks_projects.time.stamped_notice")), 1)) : i("", !0), c(v, { label: S(m)("tasks_projects.time.fields.task"), error: w.value.task_id, @@ -1872,7 +1872,7 @@ var W = { searchable: "", "preserve-search": "", "resolve-on-load": "", - "onUpdate:modelValue": l[1] ||= (e) => ee(e) + "onUpdate:modelValue": l[1] ||= (e) => R(e) }, null, 8, [ "model-value", "disabled", @@ -1900,13 +1900,13 @@ var W = { ])]), _: 1 }, 8, ["label", "error"]), c(v, { label: S(m)("tasks_projects.time.fields.mode") }, { - default: E(() => [o("div", Nn, [(g(), a(e, null, y(p, (e) => o("button", { + default: E(() => [o("div", Mn, [(g(), a(e, null, y(p, (e) => o("button", { key: e, type: "button", class: f(["px-3 py-2 text-sm", h.mode === e ? "bg-primary-500 text-white" : "bg-surface text-body hover:bg-hover"]), disabled: j.value, onClick: (t) => h.mode = e - }, x(S(m)(`tasks_projects.time.mode.${e}`)), 11, Pn)), 64))])]), + }, x(S(m)(`tasks_projects.time.mode.${e}`)), 11, Nn)), 64))])]), _: 1 }, 8, ["label"])]), _: 1 @@ -1995,7 +1995,7 @@ var W = { label: S(m)("tasks_projects.time.fields.billable"), error: w.value.billable }, { - default: E(() => [j.value ? (g(), a("span", Fn, x(h.billable ? S(m)("tasks_projects.time.billable") : S(m)("tasks_projects.time.non_billable")), 1)) : (g(), r(R, { + default: E(() => [j.value ? (g(), a("span", Pn, x(h.billable ? S(m)("tasks_projects.time.billable") : S(m)("tasks_projects.time.non_billable")), 1)) : (g(), r(z, { key: 0, modelValue: h.billable, "onUpdate:modelValue": l[6] ||= (e) => h.billable = e, @@ -2003,25 +2003,25 @@ var W = { }, null, 8, ["modelValue"]))]), _: 1 }, 8, ["label", "error"]) - ]), o("div", In, [A.value && !j.value ? (g(), r(B, { + ]), o("div", Fn, [A.value && !j.value ? (g(), r(H, { key: 0, type: "button", variant: "danger", size: "sm", loading: O.value, disabled: O.value, - onClick: te + onClick: V }, { default: E(() => [s(x(S(m)("tasks_projects.general.delete")), 1)]), _: 1 - }, 8, ["loading", "disabled"])) : (g(), a("span", Ln)), o("div", Rn, [c(B, { + }, 8, ["loading", "disabled"])) : (g(), a("span", In)), o("div", Ln, [c(H, { type: "button", variant: "primary-outline", onClick: l[7] ||= (e) => d("close") }, { default: E(() => [s(x(j.value ? S(m)("tasks_projects.timer.close") : S(m)("tasks_projects.general.cancel")), 1)]), _: 1 - }), j.value ? i("", !0) : (g(), r(B, { + }), j.value ? i("", !0) : (g(), r(H, { key: 0, type: "submit", variant: "primary", @@ -2035,25 +2035,25 @@ var W = { }, 8, ["show"]); }; } -}), Vn = { class: "mt-4 flex flex-wrap items-center justify-between gap-3" }, Hn = { class: "flex items-center gap-2" }, Un = { class: "ml-1 text-sm text-muted" }, Wn = { class: "flex items-center gap-2 text-sm" }, Gn = { class: "text-muted" }, Kn = { class: "text-lg font-semibold tabular-nums text-heading" }, qn = { +}), Bn = { class: "mt-4 flex flex-wrap items-center justify-between gap-3" }, Vn = { class: "flex items-center gap-2" }, Hn = { class: "ml-1 text-sm text-muted" }, Un = { class: "flex items-center gap-2 text-sm" }, Wn = { class: "text-muted" }, Gn = { class: "text-lg font-semibold tabular-nums text-heading" }, Kn = { key: 0, class: "mt-6 text-sm text-muted" -}, Jn = { +}, qn = { key: 1, class: "mt-4 grid gap-3 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-7" -}, Yn = { class: "flex items-baseline justify-between" }, Xn = { class: "text-xs font-semibold tracking-wide text-heading uppercase" }, Zn = { class: "text-xs text-muted" }, Qn = { class: "text-sm font-medium tabular-nums text-heading" }, $n = { class: "mt-3 flex-1 space-y-2" }, er = ["onClick"], tr = { class: "flex items-center justify-between gap-2" }, nr = { class: "truncate text-xs font-medium text-heading" }, rr = { class: "shrink-0 text-xs tabular-nums text-muted" }, ir = { +}, Jn = { class: "flex items-baseline justify-between" }, Yn = { class: "text-xs font-semibold tracking-wide text-heading uppercase" }, Xn = { class: "text-xs text-muted" }, Zn = { class: "text-sm font-medium tabular-nums text-heading" }, Qn = { class: "mt-3 flex-1 space-y-2" }, $n = ["onClick"], er = { class: "flex items-center justify-between gap-2" }, tr = { class: "truncate text-xs font-medium text-heading" }, nr = { class: "shrink-0 text-xs tabular-nums text-muted" }, rr = { key: 0, class: "mt-1 block truncate text-xs text-muted" -}, ar = { class: "mt-1 flex items-center gap-1" }, or = { class: "text-[11px] text-subtle" }, sr = { +}, ir = { class: "mt-1 flex items-center gap-1" }, ar = { class: "text-[11px] text-subtle" }, or = { key: 0, class: "text-[11px] text-subtle" -}, cr = { +}, sr = { key: 0, class: "py-2 text-xs text-subtle" -}, lr = ["onClick"], ur = { +}, cr = ["onClick"], lr = { key: 2, class: "mt-4 text-center text-sm text-subtle" -}, dr = /* @__PURE__ */ l({ +}, ur = /* @__PURE__ */ l({ __name: "WeekTimesheet", props: { client: { type: [Function, Object] }, @@ -2064,22 +2064,22 @@ var W = { }, emits: ["add", "edit"], setup(t, { emit: l }) { - let u = t, d = l, p = H(), m = v(At(/* @__PURE__ */ new Date(), u.weekStart)), h = v([]), _ = v(!1), C = n(() => jt(m.value)), w = n(() => { + let u = t, d = l, p = de(), m = v(kt(/* @__PURE__ */ new Date(), u.weekStart)), h = v([]), _ = v(!1), C = n(() => At(m.value)), w = n(() => { let e = C.value[0], t = C.value[C.value.length - 1]; - return `${Pt(e).day} - ${Pt(t).day}`; + return `${Nt(e).day} - ${Nt(t).day}`; }), D = n(() => C.value.map((e) => { - let t = Nt(e), n = h.value.filter((e) => Et(e.started_at) === t), r = Pt(e); + let t = Mt(e), n = h.value.filter((e) => Tt(e.started_at) === t), r = Nt(e); return { key: t, weekday: r.weekday, day: r.day, - today: Ft(e), + today: Pt(e), entries: n, minutes: A(n) }; })), O = n(() => A(h.value)), k = n(() => !_.value && h.value.length === 0); T(() => u.weekStart, (e) => { - m.value = At(m.value, e); + m.value = kt(m.value, e); }), T([ m, () => u.userId, @@ -2095,28 +2095,28 @@ var W = { } _.value = !0; try { - let e = await Ke(u.client, { + let e = await Ge(u.client, { user_id: u.userId, - from: Nt(C.value[0]), - to: Nt(C.value[C.value.length - 1]) + from: Mt(C.value[0]), + to: Mt(C.value[C.value.length - 1]) }); - h.value = e, ht(u.client, e.map((e) => e.task_id).filter((e) => typeof e == "number")); + h.value = e, mt(u.client, e.map((e) => e.task_id).filter((e) => typeof e == "number")); } catch (e) { - h.value = [], u.notify("error", V(e, p("tasks_projects.time.load_failed"))); + h.value = [], u.notify("error", U(e, p("tasks_projects.time.load_failed"))); } finally { _.value = !1; } } function M(e) { - m.value = Mt(m.value, e * 7); + m.value = jt(m.value, e * 7); } function N() { - m.value = At(/* @__PURE__ */ new Date(), u.weekStart); + m.value = kt(/* @__PURE__ */ new Date(), u.weekStart); } return (n, l) => { let u = b("BaseIcon"), m = b("BaseButton"), h = b("BaseSpinner"); return g(), a("section", null, [ - o("header", Vn, [o("div", Hn, [ + o("header", Bn, [o("div", Vn, [ c(m, { variant: "white", size: "sm", @@ -2149,33 +2149,33 @@ var W = { })]), _: 1 }, 8, ["title"]), - o("span", Un, x(w.value), 1) - ]), o("div", Wn, [ - o("span", Gn, x(S(p)("tasks_projects.time.week_total")), 1), - o("span", Kn, x(S(wt)(O.value)), 1), + o("span", Hn, x(w.value), 1) + ]), o("div", Un, [ + o("span", Wn, x(S(p)("tasks_projects.time.week_total")), 1), + o("span", Gn, x(S(Ct)(O.value)), 1), _.value ? (g(), r(h, { key: 0, class: "h-4 w-4 text-primary-500" })) : i("", !0) ])]), - t.userId === null ? (g(), a("p", qn, x(S(p)("tasks_projects.time.unknown_user")), 1)) : (g(), a("div", Jn, [(g(!0), a(e, null, y(D.value, (t) => (g(), a("article", { + t.userId === null ? (g(), a("p", Kn, x(S(p)("tasks_projects.time.unknown_user")), 1)) : (g(), a("div", qn, [(g(!0), a(e, null, y(D.value, (t) => (g(), a("article", { key: t.key, class: f(["flex min-h-40 flex-col rounded-xl border bg-surface p-3", t.today ? "border-primary-400" : "border-line-default"]) }, [ - o("header", Yn, [o("div", null, [o("p", Xn, x(t.weekday), 1), o("p", Zn, x(t.day), 1)]), o("span", Qn, x(S(wt)(t.minutes)), 1)]), - o("ul", $n, [(g(!0), a(e, null, y(t.entries, (e) => (g(), a("li", { key: e.id }, [o("button", { + o("header", Jn, [o("div", null, [o("p", Yn, x(t.weekday), 1), o("p", Xn, x(t.day), 1)]), o("span", Zn, x(S(Ct)(t.minutes)), 1)]), + o("ul", Qn, [(g(!0), a(e, null, y(t.entries, (e) => (g(), a("li", { key: e.id }, [o("button", { type: "button", class: "w-full rounded-md border border-line-light px-2 py-2 text-left hover:bg-hover", onClick: (t) => d("edit", e) }, [ - o("span", tr, [o("span", nr, x(S(pt)(e.task_id)), 1), o("span", rr, x(S(wt)(e.duration_minutes)), 1)]), - e.description ? (g(), a("span", ir, x(e.description), 1)) : i("", !0), - o("span", ar, [ + o("span", er, [o("span", tr, x(S(ft)(e.task_id)), 1), o("span", nr, x(S(Ct)(e.duration_minutes)), 1)]), + e.description ? (g(), a("span", rr, x(e.description), 1)) : i("", !0), + o("span", ir, [ o("span", { class: f(["inline-block h-1.5 w-1.5 rounded-full", e.billable ? "bg-status-green" : "bg-line-strong"]) }, null, 2), - o("span", or, x(e.billable ? S(p)("tasks_projects.time.billable") : S(p)("tasks_projects.time.non_billable")), 1), - e.invoice_id === null ? i("", !0) : (g(), a("span", sr, " - " + x(S(p)("tasks_projects.time.billed")), 1)) + o("span", ar, x(e.billable ? S(p)("tasks_projects.time.billable") : S(p)("tasks_projects.time.non_billable")), 1), + e.invoice_id === null ? i("", !0) : (g(), a("span", or, " - " + x(S(p)("tasks_projects.time.billed")), 1)) ]) - ], 8, er)]))), 128)), t.entries.length === 0 ? (g(), a("li", cr, x(S(p)("tasks_projects.time.no_entries")), 1)) : i("", !0)]), + ], 8, $n)]))), 128)), t.entries.length === 0 ? (g(), a("li", sr, x(S(p)("tasks_projects.time.no_entries")), 1)) : i("", !0)]), o("button", { type: "button", class: "mt-2 flex items-center justify-center gap-1 rounded-md border border-dashed border-line-default py-1.5 text-xs text-muted hover:bg-hover hover:text-heading", @@ -2183,13 +2183,13 @@ var W = { }, [c(u, { name: "PlusIcon", class: "h-4 w-4" - }), s(" " + x(S(p)("tasks_projects.time.add_entry")), 1)], 8, lr) + }), s(" " + x(S(p)("tasks_projects.time.add_entry")), 1)], 8, cr) ], 2))), 128))])), - k.value && t.userId !== null ? (g(), a("p", ur, x(S(p)("tasks_projects.time.empty_description")), 1)) : i("", !0) + k.value && t.userId !== null ? (g(), a("p", lr, x(S(p)("tasks_projects.time.empty_description")), 1)) : i("", !0) ]); }; } -}), fr = { +}), dr = { default_rate: 0, rounding_minutes: 1, week_start: 1, @@ -2200,51 +2200,51 @@ var W = { 15, 30 ] -}, pr = _({ +}, fr = _({ adminMode: !1, userId: null, - settings: { ...fr }, + settings: { ...dr }, companySession: 0, loading: !1 }); -async function mr(e) { - if (pr.adminMode) return; - pr.loading = !0; - let [t, n] = await Promise.all([lt(e).catch(() => null), ct(e).catch(() => null)]); - pr.userId = t, pr.settings = _r(n), pr.loading = !1; +async function pr(e) { + if (fr.adminMode) return; + fr.loading = !0; + let [t, n] = await Promise.all([ct(e).catch(() => null), st(e).catch(() => null)]); + fr.userId = t, fr.settings = gr(n), fr.loading = !1; } -function hr() { - pr.userId = null, pr.settings = { ...fr }, pr.companySession += 1, pr.loading = !1; +function mr() { + fr.userId = null, fr.settings = { ...dr }, fr.companySession += 1, fr.loading = !1; } -function gr(e) { - pr.adminMode = e; +function hr(e) { + fr.adminMode = e; } -function _r(e) { - if (typeof e != "object" || !e) return { ...fr }; - let t = Array.isArray(e.rounding_increments) ? e.rounding_increments.filter((e) => typeof e == "number") : fr.rounding_increments; +function gr(e) { + if (typeof e != "object" || !e) return { ...dr }; + let t = Array.isArray(e.rounding_increments) ? e.rounding_increments.filter((e) => typeof e == "number") : dr.rounding_increments; return { - default_rate: vr(e.default_rate, fr.default_rate), - rounding_minutes: vr(e.rounding_minutes, fr.rounding_minutes), - week_start: yr(e.week_start), + default_rate: _r(e.default_rate, dr.default_rate), + rounding_minutes: _r(e.rounding_minutes, dr.rounding_minutes), + week_start: vr(e.week_start), members_see_all_time: e.members_see_all_time === !0, - rounding_increments: t.length > 0 ? t : fr.rounding_increments + rounding_increments: t.length > 0 ? t : dr.rounding_increments }; } -function vr(e, t) { +function _r(e, t) { return typeof e == "number" && Number.isFinite(e) ? e : t; } -function yr(e) { - return typeof e == "number" && Number.isInteger(e) && e >= 0 && e <= 6 ? e : fr.week_start; +function vr(e) { + return typeof e == "number" && Number.isInteger(e) && e >= 0 && e <= 6 ? e : dr.week_start; } //#endregion //#region resources/js/pages/TimePage.vue?vue&type=script&setup=true&lang.ts -var br = { class: "flex items-center justify-end space-x-5" }, xr = { +var yr = { class: "flex items-center justify-end space-x-5" }, br = { key: 0, - class: "hidden items-center gap-2 text-sm text-muted sm:flex" -}, Sr = { + class: "max-sm:hidden flex items-center gap-2 text-sm text-muted" +}, xr = { key: 0, class: "mt-4 flex gap-6 border-b border-line-default" -}, Cr = 5, wr = /* @__PURE__ */ l({ +}, Sr = 5, Cr = /* @__PURE__ */ l({ __name: "TimePage", props: { client: { type: [Function, Object] }, @@ -2252,35 +2252,38 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { router: {} }, setup(e) { - let t = e, l = H(), u = v("MINE"), d = v(!1), p = v([]), m = v([]), _ = v(!1), y = v(null), C = v(Nt(/* @__PURE__ */ new Date())), w = v(0), T = n(() => pr.settings.week_start), D = n(() => pr.userId); + let t = e, l = de(), u = v("MINE"), d = v(!1), p = v([]), m = v([]), _ = v(!1), y = v(null), C = v(Mt(/* @__PURE__ */ new Date())), w = v(0), T = n(() => fr.settings.week_start), D = n(() => fr.userId); h(() => void O()); async function O() { - pr.userId === null && await mr(t.client), d.value = pr.settings.members_see_all_time || await k(), d.value && await Promise.all([A(), j()]); + fr.userId === null && await pr(t.client), d.value = fr.settings.members_see_all_time || await k(), d.value && await Promise.all([A(), j()]); } async function k() { try { - return ((await Ge(t.client, { limit: Cr })).data ?? []).some((e) => e.user_id !== pr.userId); + return ((await We(t.client, { limit: Sr })).data ?? []).some((e) => e.user_id !== fr.userId); } catch { return !1; } } async function A() { try { - p.value = await st(t.client); + p.value = await ot(t.client); } catch { p.value = []; } } async function j() { try { - let e = await F(t.client, { limit: 100 }); + let e = await F(t.client, { + limit: 100, + sort_by: "name" + }); m.value = e.data ?? []; } catch { m.value = []; } } function M(e) { - y.value = null, C.value = e ?? Nt(/* @__PURE__ */ new Date()), _.value = !0; + y.value = null, C.value = e ?? Mt(/* @__PURE__ */ new Date()), _.value = !0; } function N(e) { y.value = e, _.value = !0; @@ -2294,33 +2297,47 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { } async function L(e) { if (window.confirm(l("tasks_projects.time.delete_confirm"))) try { - await Ye(t.client, e.id), t.notify("success", l("tasks_projects.time.deleted")), w.value += 1; + await Je(t.client, e.id), t.notify("success", l("tasks_projects.time.deleted")), w.value += 1; } catch (e) { - t.notify("error", V(e, l("tasks_projects.time.delete_failed"))); + t.notify("error", U(e, l("tasks_projects.time.delete_failed"))); } } - function ee(e) { + function R(e) { return u.value === e ? "border-primary-500 text-primary-500" : "border-transparent text-muted hover:border-line-strong hover:text-heading"; } return (t, n) => { - let h = b("BaseBreadcrumbItem"), v = b("BaseBreadcrumb"), O = b("BaseIcon"), k = b("BaseButton"), A = b("BasePageHeader"), j = b("BasePage"); - return g(), r(j, null, { + let h = b("BaseBreadcrumbItem"), v = b("BaseBreadcrumb"), O = b("BaseIcon"), k = b("BaseButton"), A = b("router-link"), j = b("BasePageHeader"), F = b("BasePage"); + return g(), r(F, null, { default: E(() => [ - c(A, { title: S(l)("tasks_projects.time.title") }, { - actions: E(() => [o("div", br, [S(W).running === null ? i("", !0) : (g(), a("span", xr, [c(O, { - name: "ClockIcon", - class: "h-4 w-4 text-primary-500" - }), s(" " + x(S(l)("tasks_projects.timer.running")), 1)])), c(k, { - variant: "primary", - onClick: n[0] ||= (e) => M() - }, { - left: E((e) => [c(O, { - name: "PlusIcon", - class: f(e.class) - }, null, 8, ["class"])]), - default: E(() => [s(" " + x(S(l)("tasks_projects.time.add_entry")), 1)]), - _: 1 - })])]), + c(j, { title: S(l)("tasks_projects.time.title") }, { + actions: E(() => [o("div", yr, [ + S(G).running === null ? i("", !0) : (g(), a("span", br, [c(O, { + name: "ClockIcon", + class: "h-4 w-4 text-primary-500" + }), s(" " + x(S(l)("tasks_projects.timer.running")), 1)])), + c(A, { to: "/admin/modules/tasks-projects/billing" }, { + default: E(() => [c(k, { variant: "white" }, { + left: E((e) => [c(O, { + name: "BanknotesIcon", + class: f(e.class) + }, null, 8, ["class"])]), + default: E(() => [s(" " + x(S(l)("tasks_projects.billing.invoice_time")), 1)]), + _: 1 + })]), + _: 1 + }), + c(k, { + variant: "primary", + onClick: n[0] ||= (e) => M() + }, { + left: E((e) => [c(O, { + name: "PlusIcon", + class: f(e.class) + }, null, 8, ["class"])]), + default: E(() => [s(" " + x(S(l)("tasks_projects.time.add_entry")), 1)]), + _: 1 + }) + ])]), default: E(() => [c(v, null, { default: E(() => [ c(h, { @@ -2341,16 +2358,16 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { })]), _: 1 }, 8, ["title"]), - d.value ? (g(), a("nav", Sr, [o("button", { + d.value ? (g(), a("nav", xr, [o("button", { type: "button", - class: f(["-mb-px border-b-2 px-1 pb-3 text-sm font-medium", ee("MINE")]), + class: f(["-mb-px border-b-2 px-1 pb-3 text-sm font-medium", R("MINE")]), onClick: n[1] ||= (e) => u.value = "MINE" }, x(S(l)("tasks_projects.time.my_time")), 3), o("button", { type: "button", - class: f(["-mb-px border-b-2 px-1 pb-3 text-sm font-medium", ee("ALL")]), + class: f(["-mb-px border-b-2 px-1 pb-3 text-sm font-medium", R("ALL")]), onClick: n[2] ||= (e) => u.value = "ALL" }, x(S(l)("tasks_projects.time.all_time")), 3)])) : i("", !0), - u.value === "MINE" ? (g(), r(dr, { + u.value === "MINE" ? (g(), r(ur, { key: 1, client: e.client, notify: e.notify, @@ -2365,7 +2382,7 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { "user-id", "week-start", "reload-token" - ])) : (g(), r(kn, { + ])) : (g(), r(On, { key: 2, client: e.client, notify: e.notify, @@ -2381,7 +2398,7 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { "projects", "reload-token" ])), - c(Bn, { + c(zn, { show: _.value, client: e.client, notify: e.notify, @@ -2402,47 +2419,47 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { }); }; } -}), Tr = { +}), wr = { key: 0, class: "text-sm text-muted" -}, Er = { key: 1 }, Dr = { +}, Tr = { key: 1 }, Er = { key: 0, class: "flex items-center gap-2 text-sm text-muted" -}, Or = { +}, Dr = { key: 1, class: "text-sm text-muted" -}, kr = { +}, Or = { key: 2, class: "divide-y divide-line-light" -}, Ar = { +}, kr = { key: 0, class: "space-y-3" -}, jr = { class: "flex flex-wrap items-center gap-2" }, Mr = ["aria-label", "onClick"], Nr = { class: "flex flex-wrap items-center gap-6" }, Pr = { class: "flex items-center gap-2 text-sm text-body" }, Fr = { class: "flex items-center gap-2 text-sm text-body" }, Ir = { class: "flex gap-3" }, Lr = { +}, Ar = { class: "flex flex-wrap items-center gap-2" }, jr = ["aria-label", "onClick"], Mr = { class: "flex flex-wrap items-center gap-6" }, Nr = { class: "flex items-center gap-2 text-sm text-body" }, Pr = { class: "flex items-center gap-2 text-sm text-body" }, Fr = { class: "flex gap-3" }, Ir = { key: 1, class: "flex items-center gap-3" -}, Rr = { class: "min-w-0 flex-1 truncate text-sm font-medium text-heading" }, zr = { class: "flex items-center gap-1" }, Br = [ +}, Lr = { class: "min-w-0 flex-1 truncate text-sm font-medium text-heading" }, Rr = { class: "flex items-center gap-1" }, zr = [ "disabled", "title", "aria-label", "onClick" -], Vr = [ +], Br = [ "disabled", "title", "aria-label", "onClick" -], Hr = [ +], Vr = [ "title", "aria-label", "onClick" -], Ur = [ +], Hr = [ "disabled", "title", "aria-label", "onClick" -], Wr = { +], Ur = { key: 3, class: "mt-4 space-y-3 rounded-lg border border-line-default p-3" -}, Gr = { class: "flex flex-wrap items-center gap-2" }, Kr = ["aria-label", "onClick"], qr = { class: "flex flex-wrap items-center gap-6" }, Jr = { class: "flex items-center gap-2 text-sm text-body" }, Yr = { class: "flex items-center gap-2 text-sm text-body" }, Xr = { class: "flex gap-3" }, Zr = /* @__PURE__ */ l({ +}, Wr = { class: "flex flex-wrap items-center gap-2" }, Gr = ["aria-label", "onClick"], Kr = { class: "flex flex-wrap items-center gap-6" }, qr = { class: "flex items-center gap-2 text-sm text-body" }, Jr = { class: "flex items-center gap-2 text-sm text-body" }, Yr = { class: "flex gap-3" }, Xr = /* @__PURE__ */ l({ __name: "TaskStatusEditor", props: { client: { type: [Function, Object] }, @@ -2458,7 +2475,7 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { "#a855f7", "#0891b2", "#64748b" - ], d = H(), m = v([]), C = v(!0), w = v(!1), T = v(!1), D = v(null), O = v(!1), k = _({ + ], d = de(), m = v([]), C = v(!0), w = v(!1), T = v(!1), D = v(null), O = v(!1), k = _({ name: "", colour: "", is_default: !1, @@ -2468,9 +2485,9 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { async function j() { C.value = !0; try { - m.value = await et(l.client), w.value = !1; + m.value = await $e(l.client), w.value = !1; } catch (e) { - m.value = [], w.value = yt(e), w.value || l.notify("error", V(e, d("tasks_projects.settings.load_failed"))); + m.value = [], w.value = vt(e), w.value || l.notify("error", U(e, d("tasks_projects.settings.load_failed"))); } finally { C.value = !1; } @@ -2501,9 +2518,9 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { let e = D.value, t = k.name.trim(); T.value = !0; try { - e === null ? (await tt(l.client, F()), l.notify("success", d("tasks_projects.settings.status_created", { name: t }))) : (await nt(l.client, e, F()), l.notify("success", d("tasks_projects.settings.status_updated", { name: t }))), P(), await j(); + e === null ? (await et(l.client, F()), l.notify("success", d("tasks_projects.settings.status_created", { name: t }))) : (await tt(l.client, e, F()), l.notify("success", d("tasks_projects.settings.status_updated", { name: t }))), P(), await j(); } catch (e) { - l.notify("error", V(e, d("tasks_projects.settings.save_failed"))); + l.notify("error", U(e, d("tasks_projects.settings.save_failed"))); } finally { T.value = !1; } @@ -2512,33 +2529,33 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { if (!T.value && window.confirm(d("tasks_projects.settings.status_delete_confirm", { name: e.name }))) { T.value = !0; try { - await rt(l.client, e.id), l.notify("success", d("tasks_projects.settings.status_deleted", { name: e.name })), P(), await j(); + await nt(l.client, e.id), l.notify("success", d("tasks_projects.settings.status_deleted", { name: e.name })), P(), await j(); } catch (e) { - l.notify("error", V(e, d("tasks_projects.settings.delete_failed"))); + l.notify("error", U(e, d("tasks_projects.settings.delete_failed"))); } finally { T.value = !1; } } } - async function ee(e, t) { + async function R(e, t) { let n = e + t; if (T.value || n < 0 || n >= m.value.length) return; let r = [...m.value]; r.splice(n, 0, ...r.splice(e, 1)), m.value = r, T.value = !0; try { - m.value = await it(l.client, r.map((e) => e.id)), l.notify("success", d("tasks_projects.settings.status_reordered")); + m.value = await rt(l.client, r.map((e) => e.id)), l.notify("success", d("tasks_projects.settings.status_reordered")); } catch (e) { - l.notify("error", V(e, d("tasks_projects.settings.reorder_failed"))), await j(); + l.notify("error", U(e, d("tasks_projects.settings.reorder_failed"))), await j(); } finally { T.value = !1; } } return (t, n) => { - let l = b("BaseSpinner"), h = b("BaseInput"), _ = b("BaseInputGroup"), v = b("BaseSwitch"), j = b("BaseButton"), F = b("BaseBadge"), R = b("BaseIcon"); - return g(), a("div", null, [w.value ? (g(), a("p", Tr, x(S(d)("tasks_projects.settings.forbidden")), 1)) : (g(), a("div", Er, [C.value ? (g(), a("div", Dr, [c(l, { class: "h-4 w-4 text-primary-500" })])) : A.value ? (g(), a("p", Or, x(S(d)("tasks_projects.settings.no_statuses")), 1)) : (g(), a("ul", kr, [(g(!0), a(e, null, y(m.value, (t, l) => (g(), a("li", { + let l = b("BaseSpinner"), h = b("BaseInput"), _ = b("BaseInputGroup"), v = b("BaseSwitch"), j = b("BaseButton"), F = b("BaseBadge"), z = b("BaseIcon"); + return g(), a("div", null, [w.value ? (g(), a("p", wr, x(S(d)("tasks_projects.settings.forbidden")), 1)) : (g(), a("div", Tr, [C.value ? (g(), a("div", Er, [c(l, { class: "h-4 w-4 text-primary-500" })])) : A.value ? (g(), a("p", Dr, x(S(d)("tasks_projects.settings.no_statuses")), 1)) : (g(), a("ul", Or, [(g(!0), a(e, null, y(m.value, (t, l) => (g(), a("li", { key: t.id, class: "py-3" - }, [D.value === t.id ? (g(), a("div", Ar, [ + }, [D.value === t.id ? (g(), a("div", kr, [ c(_, { label: S(d)("tasks_projects.settings.status_name"), required: "" @@ -2552,30 +2569,30 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { _: 1 }, 8, ["label"]), c(_, { label: S(d)("tasks_projects.settings.colour") }, { - default: E(() => [o("div", jr, [(g(), a(e, null, y(u, (e) => o("button", { + default: E(() => [o("div", Ar, [(g(), a(e, null, y(u, (e) => o("button", { key: e, type: "button", class: f(["h-7 w-7 rounded-full border-2 transition", k.colour === e ? "border-heading" : "border-line-default"]), style: p({ backgroundColor: e }), "aria-label": e, onClick: (t) => k.colour = e - }, null, 14, Mr)), 64)), o("button", { + }, null, 14, jr)), 64)), o("button", { type: "button", class: "rounded-md border border-line-default px-2 py-1 text-xs text-muted hover:bg-hover", onClick: n[1] ||= (e) => k.colour = "" }, x(S(d)("tasks_projects.settings.colour_none")), 1)])]), _: 1 }, 8, ["label"]), - o("div", Nr, [o("label", Pr, [c(v, { + o("div", Mr, [o("label", Nr, [c(v, { modelValue: k.is_default, "onUpdate:modelValue": n[2] ||= (e) => k.is_default = e, class: "flex" - }, null, 8, ["modelValue"]), s(" " + x(S(d)("tasks_projects.settings.is_default")), 1)]), o("label", Fr, [c(v, { + }, null, 8, ["modelValue"]), s(" " + x(S(d)("tasks_projects.settings.is_default")), 1)]), o("label", Pr, [c(v, { modelValue: k.is_closed, "onUpdate:modelValue": n[3] ||= (e) => k.is_closed = e, class: "flex" }, null, 8, ["modelValue"]), s(" " + x(S(d)("tasks_projects.settings.is_closed")), 1)])]), - o("div", Ir, [c(j, { + o("div", Fr, [c(j, { variant: "primary", size: "sm", disabled: T.value, @@ -2591,12 +2608,12 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { default: E(() => [s(x(S(d)("tasks_projects.general.cancel")), 1)]), _: 1 })]) - ])) : (g(), a("div", Lr, [ + ])) : (g(), a("div", Ir, [ o("span", { class: f(["inline-block h-3 w-3 shrink-0 rounded-full", t.colour ? "" : "bg-line-default"]), style: p(t.colour ? { backgroundColor: t.colour } : void 0) }, null, 6), - o("span", Rr, x(t.name), 1), + o("span", Lr, x(t.name), 1), t.is_default ? (g(), r(F, { key: 0, class: "rounded-full bg-primary-50! text-primary-500!" @@ -2611,39 +2628,39 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { default: E(() => [s(x(S(d)("tasks_projects.settings.is_closed")), 1)]), _: 1 })) : i("", !0), - o("div", zr, [ + o("div", Rr, [ o("button", { type: "button", class: "rounded p-1 text-subtle hover:bg-hover hover:text-heading disabled:opacity-40", disabled: T.value || l === 0, title: S(d)("tasks_projects.settings.move_up"), "aria-label": S(d)("tasks_projects.settings.move_up"), - onClick: (e) => ee(l, -1) - }, [c(R, { + onClick: (e) => R(l, -1) + }, [c(z, { name: "ChevronUpIcon", class: "h-4 w-4" - })], 8, Br), + })], 8, zr), o("button", { type: "button", class: "rounded p-1 text-subtle hover:bg-hover hover:text-heading disabled:opacity-40", disabled: T.value || l === m.value.length - 1, title: S(d)("tasks_projects.settings.move_down"), "aria-label": S(d)("tasks_projects.settings.move_down"), - onClick: (e) => ee(l, 1) - }, [c(R, { + onClick: (e) => R(l, 1) + }, [c(z, { name: "ChevronDownIcon", class: "h-4 w-4" - })], 8, Vr), + })], 8, Br), o("button", { type: "button", class: "rounded p-1 text-subtle hover:bg-hover hover:text-heading", title: S(d)("tasks_projects.general.edit"), "aria-label": S(d)("tasks_projects.general.edit"), onClick: (e) => M(t) - }, [c(R, { + }, [c(z, { name: "PencilIcon", class: "h-4 w-4" - })], 8, Hr), + })], 8, Vr), o("button", { type: "button", class: "rounded p-1 text-subtle hover:bg-hover hover:text-alert-error-text", @@ -2651,12 +2668,12 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { title: S(d)("tasks_projects.general.delete"), "aria-label": S(d)("tasks_projects.general.delete"), onClick: (e) => L(t) - }, [c(R, { + }, [c(z, { name: "TrashIcon", class: "h-4 w-4" - })], 8, Ur) + })], 8, Hr) ]) - ]))]))), 128))])), O.value ? (g(), a("div", Wr, [ + ]))]))), 128))])), O.value ? (g(), a("div", Ur, [ c(_, { label: S(d)("tasks_projects.settings.status_name"), required: "" @@ -2670,26 +2687,26 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { _: 1 }, 8, ["label"]), c(_, { label: S(d)("tasks_projects.settings.colour") }, { - default: E(() => [o("div", Gr, [(g(), a(e, null, y(u, (e) => o("button", { + default: E(() => [o("div", Wr, [(g(), a(e, null, y(u, (e) => o("button", { key: e, type: "button", class: f(["h-7 w-7 rounded-full border-2 transition", k.colour === e ? "border-heading" : "border-line-default"]), style: p({ backgroundColor: e }), "aria-label": e, onClick: (t) => k.colour = e - }, null, 14, Kr)), 64))])]), + }, null, 14, Gr)), 64))])]), _: 1 }, 8, ["label"]), - o("div", qr, [o("label", Jr, [c(v, { + o("div", Kr, [o("label", qr, [c(v, { modelValue: k.is_default, "onUpdate:modelValue": n[5] ||= (e) => k.is_default = e, class: "flex" - }, null, 8, ["modelValue"]), s(" " + x(S(d)("tasks_projects.settings.is_default")), 1)]), o("label", Yr, [c(v, { + }, null, 8, ["modelValue"]), s(" " + x(S(d)("tasks_projects.settings.is_default")), 1)]), o("label", Jr, [c(v, { modelValue: k.is_closed, "onUpdate:modelValue": n[6] ||= (e) => k.is_closed = e, class: "flex" }, null, 8, ["modelValue"]), s(" " + x(S(d)("tasks_projects.settings.is_closed")), 1)])]), - o("div", Xr, [c(j, { + o("div", Yr, [c(j, { variant: "primary", size: "sm", disabled: T.value, @@ -2712,7 +2729,7 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { class: "mt-4", onClick: N }, { - left: E((e) => [c(R, { + left: E((e) => [c(z, { name: "PlusIcon", class: f(e.class) }, null, 8, ["class"])]), @@ -2721,7 +2738,7 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { }))]))]); }; } -}), Qr = { class: "space-y-6" }, $r = "/admin/settings/modules", ei = /* @__PURE__ */ l({ +}), Zr = { class: "space-y-6" }, Qr = "/admin/settings/modules", $r = /* @__PURE__ */ l({ __name: "TimeSettingsPage", props: { client: { type: [Function, Object] }, @@ -2729,14 +2746,14 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { router: {} }, setup(e) { - let t = H(); + let t = de(); return (n, r) => { let i = b("BaseIcon"), o = b("BaseButton"), l = b("router-link"), u = b("BaseSettingCard"); - return g(), a("div", Qr, [c(u, { + return g(), a("div", Zr, [c(u, { title: S(t)("tasks_projects.settings.general_title"), description: S(t)("tasks_projects.settings.general_description") }, { - action: E(() => [c(l, { to: $r }, { + action: E(() => [c(l, { to: Qr }, { default: E(() => [c(o, { variant: "primary-outline", size: "sm" @@ -2755,7 +2772,7 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { title: S(t)("tasks_projects.settings.statuses_title"), description: S(t)("tasks_projects.settings.statuses_description") }, { - default: E(() => [c(Zr, { + default: E(() => [c(Xr, { client: e.client, notify: e.notify }, null, 8, ["client", "notify"])]), @@ -2763,67 +2780,67 @@ var br = { class: "flex items-center justify-end space-x-5" }, xr = { }, 8, ["title", "description"])]); }; } -}), ti = "tasks-projects", ni = `/admin/modules/${ti}/time`; -function ri(e) { - e.addMessages(wn); +}), ei = "tasks-projects", ti = `/admin/modules/${ei}/time`; +function ni(e) { + e.addMessages(Cn); let t = (t, n) => { e.notify(t, n); }, n = () => { - e.router.push(ni); + e.router.push(ti); }; e.registerPage({ id: "time", - module: ti, + module: ei, path: "time", - component: oi(e, wr), + component: ai(e, Cr), meta: { - ability: `${ti}:view-own-time`, + ability: `${ei}:view-own-time`, title: "tasks_projects.time.title" } }), e.registerHeaderAction({ - id: `${ti}.timer-chip`, + id: `${ei}.timer-chip`, priority: 30, - visible: () => W.running !== null, - component: l({ setup: () => () => d(Cn, { + visible: () => G.running !== null, + component: l({ setup: () => () => d(Sn, { client: e.client, notify: t, onOpen: n }) }) }), e.registerCompanyLayoutOverlay({ - id: `${ti}.quick-start`, - component: l({ setup: () => () => d(_n, { - key: pr.companySession, + id: `${ei}.quick-start`, + component: l({ setup: () => () => d(gn, { + key: fr.companySession, client: e.client, notify: t, - enabled: !pr.adminMode, + enabled: !fr.adminMode, onOpenTimesheet: n }) }) }), e.registerCompanySettingsPage({ - id: `${ti}.settings`, + id: `${ei}.settings`, title: "tasks_projects.settings.title", icon: "ClockIcon", - path: ti, + path: ei, priority: 70, - component: oi(e, ei) + component: ai(e, $r) }), e.on("bootstrap:completed", ({ adminMode: t }) => { - ii(e, t); + ri(e, t); }), e.on("company:changing", () => { - ai(); + ii(); }), e.on("company:changed", ({ companyId: t }) => { - ii(e, t === null); + ri(e, t === null); }); } -async function ii(e, t) { - if (gr(t), t) { - ai(); +async function ri(e, t) { + if (hr(t), t) { + ii(); return; } - await mr(e.client), await W.refresh(e.client); + await pr(e.client), await G.refresh(e.client); } -function ai() { - W.reset(), gt(), hr(); +function ii() { + G.reset(), ht(), mr(); } -function oi(e, t) { +function ai(e, t) { return l({ setup: (n, { attrs: r }) => () => d(t, { ...r, client: e.client, @@ -2835,7 +2852,7 @@ function oi(e, t) { } //#endregion //#region resources/js/messages/board.ts -var si = { en: { tasks_projects: { +var oi = { en: { tasks_projects: { board: { title: "Board", load_failed: "Unable to load the board.", @@ -2970,24 +2987,24 @@ var si = { en: { tasks_projects: { } } }; //#endregion //#region node_modules/.pnpm/sortablejs@1.15.7/node_modules/sortablejs/modular/sortable.esm.js -function ci(e, t, n) { - return (t = hi(t)) in e ? Object.defineProperty(e, t, { +function si(e, t, n) { + return (t = mi(t)) in e ? Object.defineProperty(e, t, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = n, e; } -function li() { - return li = Object.assign ? Object.assign.bind() : function(e) { +function ci() { + return ci = Object.assign ? Object.assign.bind() : function(e) { for (var t = 1; t < arguments.length; t++) { var n = arguments[t]; for (var r in n) ({}).hasOwnProperty.call(n, r) && (e[r] = n[r]); } return e; - }, li.apply(null, arguments); + }, ci.apply(null, arguments); } -function ui(e, t) { +function li(e, t) { var n = Object.keys(e); if (Object.getOwnPropertySymbols) { var r = Object.getOwnPropertySymbols(e); @@ -2997,27 +3014,27 @@ function ui(e, t) { } return n; } -function di(e) { +function ui(e) { for (var t = 1; t < arguments.length; t++) { var n = arguments[t] == null ? {} : arguments[t]; - t % 2 ? ui(Object(n), !0).forEach(function(t) { - ci(e, t, n[t]); - }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : ui(Object(n)).forEach(function(t) { + t % 2 ? li(Object(n), !0).forEach(function(t) { + si(e, t, n[t]); + }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(n)) : li(Object(n)).forEach(function(t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(n, t)); }); } return e; } -function fi(e, t) { +function di(e, t) { if (e == null) return {}; - var n, r, i = pi(e, t); + var n, r, i = fi(e, t); if (Object.getOwnPropertySymbols) { var a = Object.getOwnPropertySymbols(e); for (r = 0; r < a.length; r++) n = a[r], t.indexOf(n) === -1 && {}.propertyIsEnumerable.call(e, n) && (i[n] = e[n]); } return i; } -function pi(e, t) { +function fi(e, t) { if (e == null) return {}; var n = {}; for (var r in e) if ({}.hasOwnProperty.call(e, r)) { @@ -3026,7 +3043,7 @@ function pi(e, t) { } return n; } -function mi(e, t) { +function pi(e, t) { if (typeof e != "object" || !e) return e; var n = e[Symbol.toPrimitive]; if (n !== void 0) { @@ -3036,33 +3053,33 @@ function mi(e, t) { } return (t === "string" ? String : Number)(e); } -function hi(e) { - var t = mi(e, "string"); +function mi(e) { + var t = pi(e, "string"); return typeof t == "symbol" ? t : t + ""; } -function gi(e) { +function hi(e) { "@babel/helpers - typeof"; - return gi = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) { + return hi = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(e) { return typeof e; } : function(e) { return e && typeof Symbol == "function" && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e; - }, gi(e); + }, hi(e); } -var _i = "1.15.7"; -function vi(e) { +var gi = "1.15.7"; +function _i(e) { if (typeof window < "u" && window.navigator) return !!/*@__PURE__*/ navigator.userAgent.match(e); } -var yi = vi(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i), bi = vi(/Edge/i), xi = vi(/firefox/i), Si = vi(/safari/i) && !vi(/chrome/i) && !vi(/android/i), Ci = vi(/iP(ad|od|hone)/i), wi = vi(/chrome/i) && vi(/android/i), Ti = { +var vi = _i(/(?:Trident.*rv[ :]?11\.|msie|iemobile|Windows Phone)/i), yi = _i(/Edge/i), bi = _i(/firefox/i), xi = _i(/safari/i) && !_i(/chrome/i) && !_i(/android/i), Si = _i(/iP(ad|od|hone)/i), Ci = _i(/chrome/i) && _i(/android/i), wi = { capture: !1, passive: !1 }; -function G(e, t, n) { - e.addEventListener(t, n, !yi && Ti); -} function K(e, t, n) { - e.removeEventListener(t, n, !yi && Ti); + e.addEventListener(t, n, !vi && wi); +} +function q(e, t, n) { + e.removeEventListener(t, n, !vi && wi); } -function Ei(e, t) { +function Ti(e, t) { if (t) { if (t[0] === ">" && (t = t.substring(1)), e) try { if (e.matches) return e.matches(t); @@ -3074,41 +3091,41 @@ function Ei(e, t) { return !1; } } -function Di(e) { +function Ei(e) { return e.host && e !== document && e.host.nodeType && e.host !== e ? e.host : e.parentNode; } -function Oi(e, t, n, r) { +function Di(e, t, n, r) { if (e) { n ||= document; do { - if (t != null && (t[0] === ">" ? e.parentNode === n && Ei(e, t) : Ei(e, t)) || r && e === n) return e; + if (t != null && (t[0] === ">" ? e.parentNode === n && Ti(e, t) : Ti(e, t)) || r && e === n) return e; if (e === n) break; - } while (e = Di(e)); + } while (e = Ei(e)); } return null; } -var ki = /\s+/g; -function Ai(e, t, n) { - e && t && (e.classList ? e.classList[n ? "add" : "remove"](t) : e.className = ((" " + e.className + " ").replace(ki, " ").replace(" " + t + " ", " ") + (n ? " " + t : "")).replace(ki, " ")); +var Oi = /\s+/g; +function ki(e, t, n) { + e && t && (e.classList ? e.classList[n ? "add" : "remove"](t) : e.className = ((" " + e.className + " ").replace(Oi, " ").replace(" " + t + " ", " ") + (n ? " " + t : "")).replace(Oi, " ")); } -function q(e, t, n) { +function J(e, t, n) { var r = e && e.style; if (r) { if (n === void 0) return document.defaultView && document.defaultView.getComputedStyle ? n = document.defaultView.getComputedStyle(e, "") : e.currentStyle && (n = e.currentStyle), t === void 0 ? n : n[t]; !(t in r) && t.indexOf("webkit") === -1 && (t = "-webkit-" + t), r[t] = n + (typeof n == "string" ? "" : "px"); } } -function ji(e, t) { +function Ai(e, t) { var n = ""; if (typeof e == "string") n = e; else do { - var r = q(e, "transform"); + var r = J(e, "transform"); r && r !== "none" && (n = r + " " + n); } while (!t && (e = e.parentNode)); var i = window.DOMMatrix || window.WebKitCSSMatrix || window.CSSMatrix || window.MSCSSMatrix; return i && new i(n); } -function Mi(e, t, n) { +function ji(e, t, n) { if (e) { var r = e.getElementsByTagName(t), i = 0, a = r.length; if (n) for (; i < a; i++) n(r[i], i); @@ -3116,21 +3133,21 @@ function Mi(e, t, n) { } return []; } -function Ni() { +function Mi() { return document.scrollingElement || document.documentElement; } -function Pi(e, t, n, r, i) { +function Ni(e, t, n, r, i) { if (e.getBoundingClientRect || e === window) { var a, o, s, c, l, u, d; - if (e !== window && e.parentNode && e !== Ni() ? (a = e.getBoundingClientRect(), o = a.top, s = a.left, c = a.bottom, l = a.right, u = a.height, d = a.width) : (o = 0, s = 0, c = window.innerHeight, l = window.innerWidth, u = window.innerHeight, d = window.innerWidth), (t || n) && e !== window && (i ||= e.parentNode, !yi)) do - if (i && i.getBoundingClientRect && (q(i, "transform") !== "none" || n && q(i, "position") !== "static")) { + if (e !== window && e.parentNode && e !== Mi() ? (a = e.getBoundingClientRect(), o = a.top, s = a.left, c = a.bottom, l = a.right, u = a.height, d = a.width) : (o = 0, s = 0, c = window.innerHeight, l = window.innerWidth, u = window.innerHeight, d = window.innerWidth), (t || n) && e !== window && (i ||= e.parentNode, !vi)) do + if (i && i.getBoundingClientRect && (J(i, "transform") !== "none" || n && J(i, "position") !== "static")) { var f = i.getBoundingClientRect(); - o -= f.top + parseInt(q(i, "border-top-width")), s -= f.left + parseInt(q(i, "border-left-width")), c = o + a.height, l = s + a.width; + o -= f.top + parseInt(J(i, "border-top-width")), s -= f.left + parseInt(J(i, "border-left-width")), c = o + a.height, l = s + a.width; break; } while (i = i.parentNode); if (r && e !== window) { - var p = ji(i || e), m = p && p.a, h = p && p.d; + var p = Ai(i || e), m = p && p.a, h = p && p.d; p && (o /= h, s /= m, d /= m, u /= h, c = o + u, l = s + d); } return { @@ -3143,18 +3160,18 @@ function Pi(e, t, n, r, i) { }; } } -function Fi(e, t, n) { - for (var r = Vi(e, !0), i = Pi(e)[t]; r;) { - var a = Pi(r)[n], o = void 0; +function Pi(e, t, n) { + for (var r = Bi(e, !0), i = Ni(e)[t]; r;) { + var a = Ni(r)[n], o = void 0; if (o = n === "top" || n === "left" ? i >= a : i <= a, !o) return r; - if (r === Ni()) break; - r = Vi(r, !1); + if (r === Mi()) break; + r = Bi(r, !1); } return !1; } -function Ii(e, t, n, r) { +function Fi(e, t, n, r) { for (var i = 0, a = 0, o = e.children; a < o.length;) { - if (o[a].style.display !== "none" && o[a] !== $.ghost && (r || o[a] !== $.dragged) && Oi(o[a], n.draggable, e, !1)) { + if (o[a].style.display !== "none" && o[a] !== $.ghost && (r || o[a] !== $.dragged) && Di(o[a], n.draggable, e, !1)) { if (i === t) return o[a]; i++; } @@ -3162,96 +3179,96 @@ function Ii(e, t, n, r) { } return null; } -function Li(e, t) { - for (var n = e.lastElementChild; n && (n === $.ghost || q(n, "display") === "none" || t && !Ei(n, t));) n = n.previousElementSibling; +function Ii(e, t) { + for (var n = e.lastElementChild; n && (n === $.ghost || J(n, "display") === "none" || t && !Ti(n, t));) n = n.previousElementSibling; return n || null; } -function Ri(e, t) { +function Li(e, t) { var n = 0; if (!e || !e.parentNode) return -1; - for (; e = e.previousElementSibling;) e.nodeName.toUpperCase() !== "TEMPLATE" && e !== $.clone && (!t || Ei(e, t)) && n++; + for (; e = e.previousElementSibling;) e.nodeName.toUpperCase() !== "TEMPLATE" && e !== $.clone && (!t || Ti(e, t)) && n++; return n; } -function zi(e) { - var t = 0, n = 0, r = Ni(); +function Ri(e) { + var t = 0, n = 0, r = Mi(); if (e) do { - var i = ji(e), a = i.a, o = i.d; + var i = Ai(e), a = i.a, o = i.d; t += e.scrollLeft * a, n += e.scrollTop * o; } while (e !== r && (e = e.parentNode)); return [t, n]; } -function Bi(e, t) { +function zi(e, t) { for (var n in e) if (e.hasOwnProperty(n)) { for (var r in t) if (t.hasOwnProperty(r) && t[r] === e[n][r]) return Number(n); } return -1; } -function Vi(e, t) { - if (!e || !e.getBoundingClientRect) return Ni(); +function Bi(e, t) { + if (!e || !e.getBoundingClientRect) return Mi(); var n = e, r = !1; do if (n.clientWidth < n.scrollWidth || n.clientHeight < n.scrollHeight) { - var i = q(n); + var i = J(n); if (n.clientWidth < n.scrollWidth && (i.overflowX == "auto" || i.overflowX == "scroll") || n.clientHeight < n.scrollHeight && (i.overflowY == "auto" || i.overflowY == "scroll")) { - if (!n.getBoundingClientRect || n === document.body) return Ni(); + if (!n.getBoundingClientRect || n === document.body) return Mi(); if (r || t) return n; r = !0; } } while (n = n.parentNode); - return Ni(); + return Mi(); } -function Hi(e, t) { +function Vi(e, t) { if (e && t) for (var n in t) t.hasOwnProperty(n) && (e[n] = t[n]); return e; } -function Ui(e, t) { +function Hi(e, t) { return Math.round(e.top) === Math.round(t.top) && Math.round(e.left) === Math.round(t.left) && Math.round(e.height) === Math.round(t.height) && Math.round(e.width) === Math.round(t.width); } -var Wi; -function Gi(e, t) { +var Ui; +function Wi(e, t) { return function() { - if (!Wi) { + if (!Ui) { var n = arguments, r = this; - n.length === 1 ? e.call(r, n[0]) : e.apply(r, n), Wi = setTimeout(function() { - Wi = void 0; + n.length === 1 ? e.call(r, n[0]) : e.apply(r, n), Ui = setTimeout(function() { + Ui = void 0; }, t); } }; } -function Ki() { - clearTimeout(Wi), Wi = void 0; +function Gi() { + clearTimeout(Ui), Ui = void 0; } -function qi(e, t, n) { +function Ki(e, t, n) { e.scrollLeft += t, e.scrollTop += n; } -function Ji(e) { +function qi(e) { var t = window.Polymer, n = window.jQuery || window.Zepto; return t && t.dom ? t.dom(e).cloneNode(!0) : n ? n(e).clone(!0)[0] : e.cloneNode(!0); } -function Yi(e, t, n) { +function Ji(e, t, n) { var r = {}; return Array.from(e.children).forEach(function(i) { - if (Oi(i, t.draggable, e, !1) && !i.animated && i !== n) { - var a = Pi(i); + if (Di(i, t.draggable, e, !1) && !i.animated && i !== n) { + var a = Ni(i); r.left = Math.min(r.left ?? Infinity, a.left), r.top = Math.min(r.top ?? Infinity, a.top), r.right = Math.max(r.right ?? -Infinity, a.right), r.bottom = Math.max(r.bottom ?? -Infinity, a.bottom); } }), r.width = r.right - r.left, r.height = r.bottom - r.top, r.x = r.left, r.y = r.top, r; } -var Xi = "Sortable" + (/* @__PURE__ */ new Date()).getTime(); -function Zi() { +var Yi = "Sortable" + (/* @__PURE__ */ new Date()).getTime(); +function Xi() { var e = [], t; return { captureAnimationState: function() { e = [], this.options.animation && [].slice.call(this.el.children).forEach(function(t) { - if (q(t, "display") !== "none" && t !== $.ghost) { + if (J(t, "display") !== "none" && t !== $.ghost) { e.push({ target: t, - rect: Pi(t) + rect: Ni(t) }); - var n = di({}, e[e.length - 1].rect); + var n = ui({}, e[e.length - 1].rect); if (t.thisAnimationDuration) { - var r = ji(t, !0); + var r = Ai(t, !0); r && (n.top -= r.f, n.left -= r.e); } t.fromRect = n; @@ -3262,7 +3279,7 @@ function Zi() { e.push(t); }, removeAnimationState: function(t) { - e.splice(Bi(e, { target: t }), 1); + e.splice(zi(e, { target: t }), 1); }, animateAll: function(n) { var r = this; @@ -3272,8 +3289,8 @@ function Zi() { } var i = !1, a = 0; e.forEach(function(e) { - var t = 0, n = e.target, o = n.fromRect, s = Pi(n), c = n.prevFromRect, l = n.prevToRect, u = e.rect, d = ji(n, !0); - d && (s.top -= d.f, s.left -= d.e), n.toRect = s, n.thisAnimationDuration && Ui(c, s) && !Ui(o, s) && (u.top - s.top) / (u.left - s.left) === (o.top - s.top) / (o.left - s.left) && (t = $i(u, c, l, r.options)), Ui(s, o) || (n.prevFromRect = o, n.prevToRect = s, t ||= r.options.animation, r.animate(n, u, s, t)), t && (i = !0, a = Math.max(a, t), clearTimeout(n.animationResetTimer), n.animationResetTimer = setTimeout(function() { + var t = 0, n = e.target, o = n.fromRect, s = Ni(n), c = n.prevFromRect, l = n.prevToRect, u = e.rect, d = Ai(n, !0); + d && (s.top -= d.f, s.left -= d.e), n.toRect = s, n.thisAnimationDuration && Hi(c, s) && !Hi(o, s) && (u.top - s.top) / (u.left - s.left) === (o.top - s.top) / (o.left - s.left) && (t = Qi(u, c, l, r.options)), Hi(s, o) || (n.prevFromRect = o, n.prevToRect = s, t ||= r.options.animation, r.animate(n, u, s, t)), t && (i = !0, a = Math.max(a, t), clearTimeout(n.animationResetTimer), n.animationResetTimer = setTimeout(function() { n.animationTime = 0, n.prevFromRect = null, n.fromRect = null, n.prevToRect = null, n.thisAnimationDuration = null; }, t), n.thisAnimationDuration = t); }), clearTimeout(t), i ? t = setTimeout(function() { @@ -3282,27 +3299,27 @@ function Zi() { }, animate: function(e, t, n, r) { if (r) { - q(e, "transition", ""), q(e, "transform", ""); - var i = ji(this.el), a = i && i.a, o = i && i.d, s = (t.left - n.left) / (a || 1), c = (t.top - n.top) / (o || 1); - e.animatingX = !!s, e.animatingY = !!c, q(e, "transform", "translate3d(" + s + "px," + c + "px,0)"), this.forRepaintDummy = Qi(e), q(e, "transition", "transform " + r + "ms" + (this.options.easing ? " " + this.options.easing : "")), q(e, "transform", "translate3d(0,0,0)"), typeof e.animated == "number" && clearTimeout(e.animated), e.animated = setTimeout(function() { - q(e, "transition", ""), q(e, "transform", ""), e.animated = !1, e.animatingX = !1, e.animatingY = !1; + J(e, "transition", ""), J(e, "transform", ""); + var i = Ai(this.el), a = i && i.a, o = i && i.d, s = (t.left - n.left) / (a || 1), c = (t.top - n.top) / (o || 1); + e.animatingX = !!s, e.animatingY = !!c, J(e, "transform", "translate3d(" + s + "px," + c + "px,0)"), this.forRepaintDummy = Zi(e), J(e, "transition", "transform " + r + "ms" + (this.options.easing ? " " + this.options.easing : "")), J(e, "transform", "translate3d(0,0,0)"), typeof e.animated == "number" && clearTimeout(e.animated), e.animated = setTimeout(function() { + J(e, "transition", ""), J(e, "transform", ""), e.animated = !1, e.animatingX = !1, e.animatingY = !1; }, r); } } }; } -function Qi(e) { +function Zi(e) { return e.offsetWidth; } -function $i(e, t, n, r) { +function Qi(e, t, n, r) { return Math.sqrt((t.top - e.top) ** 2 + (t.left - e.left) ** 2) / Math.sqrt((t.top - n.top) ** 2 + (t.left - n.left) ** 2) * r.animation; } -var ea = [], ta = { initializeByDefault: !0 }, na = { +var $i = [], ea = { initializeByDefault: !0 }, ta = { mount: function(e) { - for (var t in ta) ta.hasOwnProperty(t) && !(t in e) && (e[t] = ta[t]); - ea.forEach(function(t) { + for (var t in ea) ea.hasOwnProperty(t) && !(t in e) && (e[t] = ea[t]); + $i.forEach(function(t) { if (t.pluginName === e.pluginName) throw `Sortable: Cannot mount plugin ${e.pluginName} more than once`; - }), ea.push(e); + }), $i.push(e); }, pluginEvent: function(e, t, n) { var r = this; @@ -3310,16 +3327,16 @@ var ea = [], ta = { initializeByDefault: !0 }, na = { r.eventCanceled = !0; }; var i = e + "Global"; - ea.forEach(function(r) { - t[r.pluginName] && (t[r.pluginName][i] && t[r.pluginName][i](di({ sortable: t }, n)), t.options[r.pluginName] && t[r.pluginName][e] && t[r.pluginName][e](di({ sortable: t }, n))); + $i.forEach(function(r) { + t[r.pluginName] && (t[r.pluginName][i] && t[r.pluginName][i](ui({ sortable: t }, n)), t.options[r.pluginName] && t[r.pluginName][e] && t[r.pluginName][e](ui({ sortable: t }, n))); }); }, initializePlugins: function(e, t, n, r) { - for (var i in ea.forEach(function(r) { + for (var i in $i.forEach(function(r) { var i = r.pluginName; if (e.options[i] || r.initializeByDefault) { var a = new r(e, t, e.options); - a.sortable = e, a.options = e.options, e[i] = a, li(n, a.defaults); + a.sortable = e, a.options = e.options, e[i] = a, ci(n, a.defaults); } }), e.options) if (e.options.hasOwnProperty(i)) { var a = this.modifyOption(e, i, e.options[i]); @@ -3328,35 +3345,35 @@ var ea = [], ta = { initializeByDefault: !0 }, na = { }, getEventProperties: function(e, t) { var n = {}; - return ea.forEach(function(r) { - typeof r.eventProperties == "function" && li(n, r.eventProperties.call(t[r.pluginName], e)); + return $i.forEach(function(r) { + typeof r.eventProperties == "function" && ci(n, r.eventProperties.call(t[r.pluginName], e)); }), n; }, modifyOption: function(e, t, n) { var r; - return ea.forEach(function(i) { + return $i.forEach(function(i) { e[i.pluginName] && i.optionListeners && typeof i.optionListeners[t] == "function" && (r = i.optionListeners[t].call(e[i.pluginName], n)); }), r; } }; -function ra(e) { +function na(e) { var t = e.sortable, n = e.rootEl, r = e.name, i = e.targetEl, a = e.cloneEl, o = e.toEl, s = e.fromEl, c = e.oldIndex, l = e.newIndex, u = e.oldDraggableIndex, d = e.newDraggableIndex, f = e.originalEvent, p = e.putSortable, m = e.extraEventProperties; - if (t ||= n && n[Xi], t) { + if (t ||= n && n[Yi], t) { var h, g = t.options, _ = "on" + r.charAt(0).toUpperCase() + r.substr(1); - window.CustomEvent && !yi && !bi ? h = new CustomEvent(r, { + window.CustomEvent && !vi && !yi ? h = new CustomEvent(r, { bubbles: !0, cancelable: !0 }) : (h = document.createEvent("Event"), h.initEvent(r, !0, !0)), h.to = o || n, h.from = s || n, h.item = i || n, h.clone = a, h.oldIndex = c, h.newIndex = l, h.oldDraggableIndex = u, h.newDraggableIndex = d, h.originalEvent = f, h.pullMode = p ? p.lastPutMode : void 0; - var v = di(di({}, m), na.getEventProperties(r, t)); + var v = ui(ui({}, m), ta.getEventProperties(r, t)); for (var y in v) h[y] = v[y]; n && n.dispatchEvent(h), g[_] && g[_].call(t, h); } } -var ia = ["evt"], aa = function(e, t) { - var n = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}, r = n.evt, i = fi(n, ia); - na.pluginEvent.bind($)(e, t, di({ - dragEl: J, - parentEl: Y, +var ra = ["evt"], ia = function(e, t) { + var n = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : {}, r = n.evt, i = di(n, ra); + ta.pluginEvent.bind($)(e, t, ui({ + dragEl: Y, + parentEl: oa, ghostEl: X, rootEl: Z, nextEl: sa, @@ -3380,7 +3397,7 @@ var ia = ["evt"], aa = function(e, t) { la = !1; }, dispatchSortableEvent: function(e) { - oa({ + aa({ sortable: t, name: e, originalEvent: r @@ -3388,11 +3405,11 @@ var ia = ["evt"], aa = function(e, t) { } }, i)); }; -function oa(e) { - ra(di({ +function aa(e) { + na(ui({ putSortable: ha, cloneEl: Q, - targetEl: J, + targetEl: Y, rootEl: Z, oldIndex: ua, oldDraggableIndex: fa, @@ -3400,14 +3417,14 @@ function oa(e) { newDraggableIndex: pa }, e)); } -var J, Y, X, Z, sa, ca, Q, la, ua, da, fa, pa, ma, ha, ga = !1, _a = !1, va = [], ya, ba, xa, Sa, Ca, wa, Ta, Ea, Da, Oa = !1, ka = !1, Aa, ja, Ma = [], Na = !1, Pa = [], Fa = typeof document < "u", Ia = Ci, La = bi || yi ? "cssFloat" : "float", Ra = Fa && !wi && !Ci && "draggable" in document.createElement("div"), za = function() { +var Y, oa, X, Z, sa, ca, Q, la, ua, da, fa, pa, ma, ha, ga = !1, _a = !1, va = [], ya, ba, xa, Sa, Ca, wa, Ta, Ea, Da, Oa = !1, ka = !1, Aa, ja, Ma = [], Na = !1, Pa = [], Fa = typeof document < "u", Ia = Si, La = yi || vi ? "cssFloat" : "float", Ra = Fa && !Ci && !Si && "draggable" in document.createElement("div"), za = function() { if (Fa) { - if (yi) return !1; + if (vi) return !1; var e = document.createElement("x"); return e.style.cssText = "pointer-events:auto", e.style.pointerEvents === "auto"; } }(), Ba = function(e, t) { - var n = q(e), r = parseInt(n.width) - parseInt(n.paddingLeft) - parseInt(n.paddingRight) - parseInt(n.borderLeftWidth) - parseInt(n.borderRightWidth), i = Ii(e, 0, t), a = Ii(e, 1, t), o = i && q(i), s = a && q(a), c = o && parseInt(o.marginLeft) + parseInt(o.marginRight) + Pi(i).width, l = s && parseInt(s.marginLeft) + parseInt(s.marginRight) + Pi(a).width; + var n = J(e), r = parseInt(n.width) - parseInt(n.paddingLeft) - parseInt(n.paddingRight) - parseInt(n.borderLeftWidth) - parseInt(n.borderRightWidth), i = Fi(e, 0, t), a = Fi(e, 1, t), o = i && J(i), s = a && J(a), c = o && parseInt(o.marginLeft) + parseInt(o.marginRight) + Ni(i).width, l = s && parseInt(s.marginLeft) + parseInt(s.marginRight) + Ni(a).width; if (n.display === "flex") return n.flexDirection === "column" || n.flexDirection === "column-reverse" ? "vertical" : "horizontal"; if (n.display === "grid") return n.gridTemplateColumns.split(" ").length <= 1 ? "vertical" : "horizontal"; if (i && o.float && o.float !== "none") { @@ -3421,9 +3438,9 @@ var J, Y, X, Z, sa, ca, Q, la, ua, da, fa, pa, ma, ha, ga = !1, _a = !1, va = [] }, Ha = function(e, t) { var n; return va.some(function(r) { - var i = r[Xi].options.emptyInsertThreshold; - if (i && !Li(r)) { - var a = Pi(r), o = e >= a.left - i && e <= a.right + i, s = t >= a.top - i && t <= a.bottom + i; + var i = r[Yi].options.emptyInsertThreshold; + if (i && !Ii(r)) { + var a = Ni(r), o = e >= a.left - i && e <= a.right + i, s = t >= a.top - i && t <= a.bottom + i; if (o && s) return n = r; } }), n; @@ -3440,31 +3457,31 @@ var J, Y, X, Z, sa, ca, Q, la, ua, da, fa, pa, ma, ha, ga = !1, _a = !1, va = [] }; } var n = {}, r = e.group; - (!r || gi(r) != "object") && (r = { name: r }), n.name = r.name, n.checkPull = t(r.pull, !0), n.checkPut = t(r.put), n.revertClone = r.revertClone, e.group = n; + (!r || hi(r) != "object") && (r = { name: r }), n.name = r.name, n.checkPull = t(r.pull, !0), n.checkPut = t(r.put), n.revertClone = r.revertClone, e.group = n; }, Wa = function() { - !za && X && q(X, "display", "none"); + !za && X && J(X, "display", "none"); }, Ga = function() { - !za && X && q(X, "display", ""); + !za && X && J(X, "display", ""); }; -Fa && !wi && document.addEventListener("click", function(e) { +Fa && !Ci && document.addEventListener("click", function(e) { if (_a) return e.preventDefault(), e.stopPropagation && e.stopPropagation(), e.stopImmediatePropagation && e.stopImmediatePropagation(), _a = !1, !1; }, !0); var Ka = function(e) { - if (J) { + if (Y) { e = e.touches ? e.touches[0] : e; var t = Ha(e.clientX, e.clientY); if (t) { var n = {}; for (var r in e) e.hasOwnProperty(r) && (n[r] = e[r]); - n.target = n.rootEl = t, n.preventDefault = void 0, n.stopPropagation = void 0, t[Xi]._onDragOver(n); + n.target = n.rootEl = t, n.preventDefault = void 0, n.stopPropagation = void 0, t[Yi]._onDragOver(n); } } }, qa = function(e) { - J && J.parentNode[Xi]._isOutsideThisEl(e.target); + Y && Y.parentNode[Yi]._isOutsideThisEl(e.target); }; function $(e, t) { if (!(e && e.nodeType && e.nodeType === 1)) throw `Sortable: \`el\` must be an HTMLElement, not ${{}.toString.call(e)}`; - this.el = e, this.options = t = li({}, t), e[Xi] = this; + this.el = e, this.options = t = ci({}, t), e[Yi] = this; var n = { group: null, sort: !0, @@ -3504,12 +3521,12 @@ function $(e, t) { x: 0, y: 0 }, - supportPointer: $.supportPointer !== !1 && "PointerEvent" in window && (!Si || Ci), + supportPointer: $.supportPointer !== !1 && "PointerEvent" in window && (!xi || Si), emptyInsertThreshold: 5 }; - for (var r in na.initializePlugins(this, e, n), n) !(r in t) && (t[r] = n[r]); + for (var r in ta.initializePlugins(this, e, n), n) !(r in t) && (t[r] = n[r]); for (var i in Ua(t), this) i.charAt(0) === "_" && typeof this[i] == "function" && (this[i] = this[i].bind(this)); - this.nativeDraggable = !t.forceFallback && Ra, this.nativeDraggable && (this.options.touchStartThreshold = 1), t.supportPointer ? G(e, "pointerdown", this._onTapStart) : (G(e, "mousedown", this._onTapStart), G(e, "touchstart", this._onTapStart)), this.nativeDraggable && (G(e, "dragover", this), G(e, "dragenter", this)), va.push(this.el), t.store && t.store.get && this.sort(t.store.get(this) || []), li(this, Zi()); + this.nativeDraggable = !t.forceFallback && Ra, this.nativeDraggable && (this.options.touchStartThreshold = 1), t.supportPointer ? K(e, "pointerdown", this._onTapStart) : (K(e, "mousedown", this._onTapStart), K(e, "touchstart", this._onTapStart)), this.nativeDraggable && (K(e, "dragover", this), K(e, "dragenter", this)), va.push(this.el), t.store && t.store.get && this.sort(t.store.get(this) || []), ci(this, Xi()); } $.prototype = { constructor: $, @@ -3517,67 +3534,67 @@ $.prototype = { !this.el.contains(e) && e !== this.el && (Ea = null); }, _getDirection: function(e, t) { - return typeof this.options.direction == "function" ? this.options.direction.call(this, e, t, J) : this.options.direction; + return typeof this.options.direction == "function" ? this.options.direction.call(this, e, t, Y) : this.options.direction; }, _onTapStart: function(e) { if (e.cancelable) { var t = this, n = this.el, r = this.options, i = r.preventOnFilter, a = e.type, o = e.touches && e.touches[0] || e.pointerType && e.pointerType === "touch" && e, s = (o || e).target, c = e.target.shadowRoot && (e.path && e.path[0] || e.composedPath && e.composedPath()[0]) || s, l = r.filter; - if (ro(n), !J && !(/mousedown|pointerdown/.test(a) && e.button !== 0 || r.disabled) && !c.isContentEditable && !(!this.nativeDraggable && Si && s && s.tagName.toUpperCase() === "SELECT") && (s = Oi(s, r.draggable, n, !1), !(s && s.animated) && ca !== s)) { - if (ua = Ri(s), fa = Ri(s, r.draggable), typeof l == "function") { + if (ro(n), !Y && !(/mousedown|pointerdown/.test(a) && e.button !== 0 || r.disabled) && !c.isContentEditable && !(!this.nativeDraggable && xi && s && s.tagName.toUpperCase() === "SELECT") && (s = Di(s, r.draggable, n, !1), !(s && s.animated) && ca !== s)) { + if (ua = Li(s), fa = Li(s, r.draggable), typeof l == "function") { if (l.call(this, e, s, this)) { - oa({ + aa({ sortable: t, rootEl: c, name: "filter", targetEl: s, toEl: n, fromEl: n - }), aa("filter", t, { evt: e }), i && e.preventDefault(); + }), ia("filter", t, { evt: e }), i && e.preventDefault(); return; } } else if (l && (l = l.split(",").some(function(r) { - if (r = Oi(c, r.trim(), n, !1), r) return oa({ + if (r = Di(c, r.trim(), n, !1), r) return aa({ sortable: t, rootEl: r, name: "filter", targetEl: s, fromEl: n, toEl: n - }), aa("filter", t, { evt: e }), !0; + }), ia("filter", t, { evt: e }), !0; }), l)) { i && e.preventDefault(); return; } - (!r.handle || Oi(c, r.handle, n, !1)) && this._prepareDragStart(e, o, s); + (!r.handle || Di(c, r.handle, n, !1)) && this._prepareDragStart(e, o, s); } } }, _prepareDragStart: function(e, t, n) { var r = this, i = r.el, a = r.options, o = i.ownerDocument, s; - if (n && !J && n.parentNode === i) { - var c = Pi(n); - if (Z = i, J = n, Y = J.parentNode, sa = J.nextSibling, ca = n, ma = a.group, $.dragged = J, ya = { - target: J, + if (n && !Y && n.parentNode === i) { + var c = Ni(n); + if (Z = i, Y = n, oa = Y.parentNode, sa = Y.nextSibling, ca = n, ma = a.group, $.dragged = Y, ya = { + target: Y, clientX: (t || e).clientX, clientY: (t || e).clientY - }, Ca = ya.clientX - c.left, wa = ya.clientY - c.top, this._lastX = (t || e).clientX, this._lastY = (t || e).clientY, J.style["will-change"] = "all", s = function() { - if (aa("delayEnded", r, { evt: e }), $.eventCanceled) { + }, Ca = ya.clientX - c.left, wa = ya.clientY - c.top, this._lastX = (t || e).clientX, this._lastY = (t || e).clientY, Y.style["will-change"] = "all", s = function() { + if (ia("delayEnded", r, { evt: e }), $.eventCanceled) { r._onDrop(); return; } - r._disableDelayedDragEvents(), !xi && r.nativeDraggable && (J.draggable = !0), r._triggerDragStart(e, t), oa({ + r._disableDelayedDragEvents(), !bi && r.nativeDraggable && (Y.draggable = !0), r._triggerDragStart(e, t), aa({ sortable: r, name: "choose", originalEvent: e - }), Ai(J, a.chosenClass, !0); + }), ki(Y, a.chosenClass, !0); }, a.ignore.split(",").forEach(function(e) { - Mi(J, e.trim(), Xa); - }), G(o, "dragover", Ka), G(o, "mousemove", Ka), G(o, "touchmove", Ka), a.supportPointer ? (G(o, "pointerup", r._onDrop), !this.nativeDraggable && G(o, "pointercancel", r._onDrop)) : (G(o, "mouseup", r._onDrop), G(o, "touchend", r._onDrop), G(o, "touchcancel", r._onDrop)), xi && this.nativeDraggable && (this.options.touchStartThreshold = 4, J.draggable = !0), aa("delayStart", this, { evt: e }), a.delay && (!a.delayOnTouchOnly || t) && (!this.nativeDraggable || !(bi || yi))) { + ji(Y, e.trim(), Xa); + }), K(o, "dragover", Ka), K(o, "mousemove", Ka), K(o, "touchmove", Ka), a.supportPointer ? (K(o, "pointerup", r._onDrop), !this.nativeDraggable && K(o, "pointercancel", r._onDrop)) : (K(o, "mouseup", r._onDrop), K(o, "touchend", r._onDrop), K(o, "touchcancel", r._onDrop)), bi && this.nativeDraggable && (this.options.touchStartThreshold = 4, Y.draggable = !0), ia("delayStart", this, { evt: e }), a.delay && (!a.delayOnTouchOnly || t) && (!this.nativeDraggable || !(yi || vi))) { if ($.eventCanceled) { this._onDrop(); return; } - a.supportPointer ? (G(o, "pointerup", r._disableDelayedDrag), G(o, "pointercancel", r._disableDelayedDrag)) : (G(o, "mouseup", r._disableDelayedDrag), G(o, "touchend", r._disableDelayedDrag), G(o, "touchcancel", r._disableDelayedDrag)), G(o, "mousemove", r._delayedDragTouchMoveHandler), G(o, "touchmove", r._delayedDragTouchMoveHandler), a.supportPointer && G(o, "pointermove", r._delayedDragTouchMoveHandler), r._dragStartTimer = setTimeout(s, a.delay); + a.supportPointer ? (K(o, "pointerup", r._disableDelayedDrag), K(o, "pointercancel", r._disableDelayedDrag)) : (K(o, "mouseup", r._disableDelayedDrag), K(o, "touchend", r._disableDelayedDrag), K(o, "touchcancel", r._disableDelayedDrag)), K(o, "mousemove", r._delayedDragTouchMoveHandler), K(o, "touchmove", r._delayedDragTouchMoveHandler), a.supportPointer && K(o, "pointermove", r._delayedDragTouchMoveHandler), r._dragStartTimer = setTimeout(s, a.delay); } else s(); } }, @@ -3586,14 +3603,14 @@ $.prototype = { Math.max(Math.abs(t.clientX - this._lastX), Math.abs(t.clientY - this._lastY)) >= Math.floor(this.options.touchStartThreshold / (this.nativeDraggable && window.devicePixelRatio || 1)) && this._disableDelayedDrag(); }, _disableDelayedDrag: function() { - J && Xa(J), clearTimeout(this._dragStartTimer), this._disableDelayedDragEvents(); + Y && Xa(Y), clearTimeout(this._dragStartTimer), this._disableDelayedDragEvents(); }, _disableDelayedDragEvents: function() { var e = this.el.ownerDocument; - K(e, "mouseup", this._disableDelayedDrag), K(e, "touchend", this._disableDelayedDrag), K(e, "touchcancel", this._disableDelayedDrag), K(e, "pointerup", this._disableDelayedDrag), K(e, "pointercancel", this._disableDelayedDrag), K(e, "mousemove", this._delayedDragTouchMoveHandler), K(e, "touchmove", this._delayedDragTouchMoveHandler), K(e, "pointermove", this._delayedDragTouchMoveHandler); + q(e, "mouseup", this._disableDelayedDrag), q(e, "touchend", this._disableDelayedDrag), q(e, "touchcancel", this._disableDelayedDrag), q(e, "pointerup", this._disableDelayedDrag), q(e, "pointercancel", this._disableDelayedDrag), q(e, "mousemove", this._delayedDragTouchMoveHandler), q(e, "touchmove", this._delayedDragTouchMoveHandler), q(e, "pointermove", this._delayedDragTouchMoveHandler); }, _triggerDragStart: function(e, t) { - t ||= e.pointerType == "touch" && e, !this.nativeDraggable || t ? this.options.supportPointer ? G(document, "pointermove", this._onTouchMove) : t ? G(document, "touchmove", this._onTouchMove) : G(document, "mousemove", this._onTouchMove) : (G(J, "dragend", this), G(Z, "dragstart", this._onDragStart)); + t ||= e.pointerType == "touch" && e, !this.nativeDraggable || t ? this.options.supportPointer ? K(document, "pointermove", this._onTouchMove) : t ? K(document, "touchmove", this._onTouchMove) : K(document, "mousemove", this._onTouchMove) : (K(Y, "dragend", this), K(Z, "dragstart", this._onDragStart)); try { document.selection ? io(function() { document.selection.empty(); @@ -3601,10 +3618,10 @@ $.prototype = { } catch {} }, _dragStarted: function(e, t) { - if (ga = !1, Z && J) { - aa("dragStarted", this, { evt: t }), this.nativeDraggable && G(document, "dragover", qa); + if (ga = !1, Z && Y) { + ia("dragStarted", this, { evt: t }), this.nativeDraggable && K(document, "dragover", qa); var n = this.options; - !e && Ai(J, n.dragClass, !1), Ai(J, n.ghostClass, !0), $.active = this, e && this._appendGhost(), oa({ + !e && ki(Y, n.dragClass, !1), ki(Y, n.ghostClass, !0), $.active = this, e && this._appendGhost(), aa({ sortable: this, name: "start", originalEvent: t @@ -3615,10 +3632,10 @@ $.prototype = { if (ba) { this._lastX = ba.clientX, this._lastY = ba.clientY, Wa(); for (var e = document.elementFromPoint(ba.clientX, ba.clientY), t = e; e && e.shadowRoot && (e = e.shadowRoot.elementFromPoint(ba.clientX, ba.clientY), e !== t);) t = e; - if (J.parentNode[Xi]._isOutsideThisEl(e), t) do { - if (t[Xi]) { + if (Y.parentNode[Yi]._isOutsideThisEl(e), t) do { + if (t[Yi]) { var n = void 0; - if (n = t[Xi]._onDragOver({ + if (n = t[Yi]._onDragOver({ clientX: ba.clientX, clientY: ba.clientY, target: e, @@ -3626,13 +3643,13 @@ $.prototype = { }), n && !this.options.dragoverBubble) break; } e = t; - } while (t = Di(t)); + } while (t = Ei(t)); Ga(); } }, _onTouchMove: function(e) { if (ya) { - var t = this.options, n = t.fallbackTolerance, r = t.fallbackOffset, i = e.touches ? e.touches[0] : e, a = X && ji(X, !0), o = X && a && a.a, s = X && a && a.d, c = Ia && ja && zi(ja), l = (i.clientX - ya.clientX + r.x) / (o || 1) + (c ? c[0] - Ma[0] : 0) / (o || 1), u = (i.clientY - ya.clientY + r.y) / (s || 1) + (c ? c[1] - Ma[1] : 0) / (s || 1); + var t = this.options, n = t.fallbackTolerance, r = t.fallbackOffset, i = e.touches ? e.touches[0] : e, a = X && Ai(X, !0), o = X && a && a.a, s = X && a && a.d, c = Ia && ja && Ri(ja), l = (i.clientX - ya.clientX + r.x) / (o || 1) + (c ? c[0] - Ma[0] : 0) / (o || 1), u = (i.clientY - ya.clientY + r.y) / (s || 1) + (c ? c[1] - Ma[1] : 0) / (s || 1); if (!$.active && !ga) { if (n && Math.max(Math.abs(i.clientX - this._lastX), Math.abs(i.clientY - this._lastY)) < n) return; this._onDragStart(e, !0); @@ -3647,39 +3664,39 @@ $.prototype = { f: u }; var d = `matrix(${a.a},${a.b},${a.c},${a.d},${a.e},${a.f})`; - q(X, "webkitTransform", d), q(X, "mozTransform", d), q(X, "msTransform", d), q(X, "transform", d), xa = l, Sa = u, ba = i; + J(X, "webkitTransform", d), J(X, "mozTransform", d), J(X, "msTransform", d), J(X, "transform", d), xa = l, Sa = u, ba = i; } e.cancelable && e.preventDefault(); } }, _appendGhost: function() { if (!X) { - var e = this.options.fallbackOnBody ? document.body : Z, t = Pi(J, !0, Ia, !0, e), n = this.options; + var e = this.options.fallbackOnBody ? document.body : Z, t = Ni(Y, !0, Ia, !0, e), n = this.options; if (Ia) { - for (ja = e; q(ja, "position") === "static" && q(ja, "transform") === "none" && ja !== document;) ja = ja.parentNode; - ja !== document.body && ja !== document.documentElement ? (ja === document && (ja = Ni()), t.top += ja.scrollTop, t.left += ja.scrollLeft) : ja = Ni(), Ma = zi(ja); + for (ja = e; J(ja, "position") === "static" && J(ja, "transform") === "none" && ja !== document;) ja = ja.parentNode; + ja !== document.body && ja !== document.documentElement ? (ja === document && (ja = Mi()), t.top += ja.scrollTop, t.left += ja.scrollLeft) : ja = Mi(), Ma = Ri(ja); } - X = J.cloneNode(!0), Ai(X, n.ghostClass, !1), Ai(X, n.fallbackClass, !0), Ai(X, n.dragClass, !0), q(X, "transition", ""), q(X, "transform", ""), q(X, "box-sizing", "border-box"), q(X, "margin", 0), q(X, "top", t.top), q(X, "left", t.left), q(X, "width", t.width), q(X, "height", t.height), q(X, "opacity", "0.8"), q(X, "position", Ia ? "absolute" : "fixed"), q(X, "zIndex", "100000"), q(X, "pointerEvents", "none"), $.ghost = X, e.appendChild(X), q(X, "transform-origin", Ca / parseInt(X.style.width) * 100 + "% " + wa / parseInt(X.style.height) * 100 + "%"); + X = Y.cloneNode(!0), ki(X, n.ghostClass, !1), ki(X, n.fallbackClass, !0), ki(X, n.dragClass, !0), J(X, "transition", ""), J(X, "transform", ""), J(X, "box-sizing", "border-box"), J(X, "margin", 0), J(X, "top", t.top), J(X, "left", t.left), J(X, "width", t.width), J(X, "height", t.height), J(X, "opacity", "0.8"), J(X, "position", Ia ? "absolute" : "fixed"), J(X, "zIndex", "100000"), J(X, "pointerEvents", "none"), $.ghost = X, e.appendChild(X), J(X, "transform-origin", Ca / parseInt(X.style.width) * 100 + "% " + wa / parseInt(X.style.height) * 100 + "%"); } }, _onDragStart: function(e, t) { var n = this, r = e.dataTransfer, i = n.options; - if (aa("dragStart", this, { evt: e }), $.eventCanceled) { + if (ia("dragStart", this, { evt: e }), $.eventCanceled) { this._onDrop(); return; } - aa("setupClone", this), $.eventCanceled || (Q = Ji(J), Q.removeAttribute("id"), Q.draggable = !1, Q.style["will-change"] = "", this._hideClone(), Ai(Q, this.options.chosenClass, !1), $.clone = Q), n.cloneId = io(function() { - aa("clone", n), !$.eventCanceled && (n.options.removeCloneOnHide || Z.insertBefore(Q, J), n._hideClone(), oa({ + ia("setupClone", this), $.eventCanceled || (Q = qi(Y), Q.removeAttribute("id"), Q.draggable = !1, Q.style["will-change"] = "", this._hideClone(), ki(Q, this.options.chosenClass, !1), $.clone = Q), n.cloneId = io(function() { + ia("clone", n), !$.eventCanceled && (n.options.removeCloneOnHide || Z.insertBefore(Q, Y), n._hideClone(), aa({ sortable: n, name: "clone" })); - }), !t && Ai(J, i.dragClass, !0), t ? (_a = !0, n._loopId = setInterval(n._emulateDragOver, 50)) : (K(document, "mouseup", n._onDrop), K(document, "touchend", n._onDrop), K(document, "touchcancel", n._onDrop), r && (r.effectAllowed = "move", i.setData && i.setData.call(n, r, J)), G(document, "drop", n), q(J, "transform", "translateZ(0)")), ga = !0, n._dragStartId = io(n._dragStarted.bind(n, t, e)), G(document, "selectstart", n), Ta = !0, window.getSelection().removeAllRanges(), Si && q(document.body, "user-select", "none"); + }), !t && ki(Y, i.dragClass, !0), t ? (_a = !0, n._loopId = setInterval(n._emulateDragOver, 50)) : (q(document, "mouseup", n._onDrop), q(document, "touchend", n._onDrop), q(document, "touchcancel", n._onDrop), r && (r.effectAllowed = "move", i.setData && i.setData.call(n, r, Y)), K(document, "drop", n), J(Y, "transform", "translateZ(0)")), ga = !0, n._dragStartId = io(n._dragStarted.bind(n, t, e)), K(document, "selectstart", n), Ta = !0, window.getSelection().removeAllRanges(), xi && J(document.body, "user-select", "none"); }, _onDragOver: function(e) { var t = this.el, n = e.target, r, i, a, o = this.options, s = o.group, c = $.active, l = ma === s, u = o.sort, d = ha || c, f, p = this, m = !1; if (Na) return; function h(o, s) { - aa(o, p, di({ + ia(o, p, ui({ evt: e, isOwner: l, axis: f ? "vertical" : "horizontal", @@ -3691,7 +3708,7 @@ $.prototype = { target: n, completed: _, onMove: function(n, i) { - return Ya(Z, t, J, r, n, Pi(n), e, i); + return Ya(Z, t, Y, r, n, Ni(n), e, i); }, changed: v }, s)); @@ -3700,12 +3717,12 @@ $.prototype = { h("dragOverAnimationCapture"), p.captureAnimationState(), p !== d && d.captureAnimationState(); } function _(r) { - return h("dragOverCompleted", { insertion: r }), r && (l ? c._hideClone() : c._showClone(p), p !== d && (Ai(J, ha ? ha.options.ghostClass : c.options.ghostClass, !1), Ai(J, o.ghostClass, !0)), ha !== p && p !== $.active ? ha = p : p === $.active && ha && (ha = null), d === p && (p._ignoreWhileAnimating = n), p.animateAll(function() { + return h("dragOverCompleted", { insertion: r }), r && (l ? c._hideClone() : c._showClone(p), p !== d && (ki(Y, ha ? ha.options.ghostClass : c.options.ghostClass, !1), ki(Y, o.ghostClass, !0)), ha !== p && p !== $.active ? ha = p : p === $.active && ha && (ha = null), d === p && (p._ignoreWhileAnimating = n), p.animateAll(function() { h("dragOverAnimationComplete"), p._ignoreWhileAnimating = null; - }), p !== d && (d.animateAll(), d._ignoreWhileAnimating = null)), (n === J && !J.animated || n === t && !n.animated) && (Ea = null), !o.dragoverBubble && !e.rootEl && n !== document && (J.parentNode[Xi]._isOutsideThisEl(e.target), !r && Ka(e)), !o.dragoverBubble && e.stopPropagation && e.stopPropagation(), m = !0; + }), p !== d && (d.animateAll(), d._ignoreWhileAnimating = null)), (n === Y && !Y.animated || n === t && !n.animated) && (Ea = null), !o.dragoverBubble && !e.rootEl && n !== document && (Y.parentNode[Yi]._isOutsideThisEl(e.target), !r && Ka(e)), !o.dragoverBubble && e.stopPropagation && e.stopPropagation(), m = !0; } function v() { - da = Ri(J), pa = Ri(J, o.draggable), oa({ + da = Li(Y), pa = Li(Y, o.draggable), aa({ sortable: p, name: "change", toEl: t, @@ -3714,103 +3731,103 @@ $.prototype = { originalEvent: e }); } - if (e.preventDefault !== void 0 && e.cancelable && e.preventDefault(), n = Oi(n, o.draggable, t, !0), h("dragOver"), $.eventCanceled) return m; - if (J.contains(e.target) || n.animated && n.animatingX && n.animatingY || p._ignoreWhileAnimating === n) return _(!1); - if (_a = !1, c && !o.disabled && (l ? u || (a = Y !== Z) : ha === this || (this.lastPutMode = ma.checkPull(this, c, J, e)) && s.checkPut(this, c, J, e))) { - if (f = this._getDirection(e, n) === "vertical", r = Pi(J), h("dragOverValid"), $.eventCanceled) return m; - if (a) return Y = Z, g(), this._hideClone(), h("revert"), $.eventCanceled || (sa ? Z.insertBefore(J, sa) : Z.appendChild(J)), _(!0); - var y = Li(t, o.draggable); + if (e.preventDefault !== void 0 && e.cancelable && e.preventDefault(), n = Di(n, o.draggable, t, !0), h("dragOver"), $.eventCanceled) return m; + if (Y.contains(e.target) || n.animated && n.animatingX && n.animatingY || p._ignoreWhileAnimating === n) return _(!1); + if (_a = !1, c && !o.disabled && (l ? u || (a = oa !== Z) : ha === this || (this.lastPutMode = ma.checkPull(this, c, Y, e)) && s.checkPut(this, c, Y, e))) { + if (f = this._getDirection(e, n) === "vertical", r = Ni(Y), h("dragOverValid"), $.eventCanceled) return m; + if (a) return oa = Z, g(), this._hideClone(), h("revert"), $.eventCanceled || (sa ? Z.insertBefore(Y, sa) : Z.appendChild(Y)), _(!0); + var y = Ii(t, o.draggable); if (!y || $a(e, f, this) && !y.animated) { - if (y === J) return _(!1); - if (y && t === e.target && (n = y), n && (i = Pi(n)), Ya(Z, t, J, r, n, i, e, !!n) !== !1) return g(), y && y.nextSibling ? t.insertBefore(J, y.nextSibling) : t.appendChild(J), Y = t, v(), _(!0); + if (y === Y) return _(!1); + if (y && t === e.target && (n = y), n && (i = Ni(n)), Ya(Z, t, Y, r, n, i, e, !!n) !== !1) return g(), y && y.nextSibling ? t.insertBefore(Y, y.nextSibling) : t.appendChild(Y), oa = t, v(), _(!0); } else if (y && Qa(e, f, this)) { - var b = Ii(t, 0, o, !0); - if (b === J) return _(!1); - if (n = b, i = Pi(n), Ya(Z, t, J, r, n, i, e, !1) !== !1) return g(), t.insertBefore(J, b), Y = t, v(), _(!0); + var b = Fi(t, 0, o, !0); + if (b === Y) return _(!1); + if (n = b, i = Ni(n), Ya(Z, t, Y, r, n, i, e, !1) !== !1) return g(), t.insertBefore(Y, b), oa = t, v(), _(!0); } else if (n.parentNode === t) { - i = Pi(n); - var x = 0, S, C = J.parentNode !== t, w = !Va(J.animated && J.toRect || r, n.animated && n.toRect || i, f), T = f ? "top" : "left", E = Fi(n, "top", "top") || Fi(J, "top", "top"), D = E ? E.scrollTop : void 0; + i = Ni(n); + var x = 0, S, C = Y.parentNode !== t, w = !Va(Y.animated && Y.toRect || r, n.animated && n.toRect || i, f), T = f ? "top" : "left", E = Pi(n, "top", "top") || Pi(Y, "top", "top"), D = E ? E.scrollTop : void 0; Ea !== n && (S = i[T], Oa = !1, ka = !w && o.invertSwap || C), x = eo(e, n, i, f, w ? 1 : o.swapThreshold, o.invertedSwapThreshold == null ? o.swapThreshold : o.invertedSwapThreshold, ka, Ea === n); var O; if (x !== 0) { - var k = Ri(J); + var k = Li(Y); do - k -= x, O = Y.children[k]; - while (O && (q(O, "display") === "none" || O === X)); + k -= x, O = oa.children[k]; + while (O && (J(O, "display") === "none" || O === X)); } if (x === 0 || O === n) return _(!1); Ea = n, Da = x; var A = n.nextElementSibling, j = !1; j = x === 1; - var M = Ya(Z, t, J, r, n, i, e, j); - if (M !== !1) return (M === 1 || M === -1) && (j = M === 1), Na = !0, setTimeout(Za, 30), g(), j && !A ? t.appendChild(J) : n.parentNode.insertBefore(J, j ? A : n), E && qi(E, 0, D - E.scrollTop), Y = J.parentNode, S !== void 0 && !ka && (Aa = Math.abs(S - Pi(n)[T])), v(), _(!0); + var M = Ya(Z, t, Y, r, n, i, e, j); + if (M !== !1) return (M === 1 || M === -1) && (j = M === 1), Na = !0, setTimeout(Za, 30), g(), j && !A ? t.appendChild(Y) : n.parentNode.insertBefore(Y, j ? A : n), E && Ki(E, 0, D - E.scrollTop), oa = Y.parentNode, S !== void 0 && !ka && (Aa = Math.abs(S - Ni(n)[T])), v(), _(!0); } - if (t.contains(J)) return _(!1); + if (t.contains(Y)) return _(!1); } return !1; }, _ignoreWhileAnimating: null, _offMoveEvents: function() { - K(document, "mousemove", this._onTouchMove), K(document, "touchmove", this._onTouchMove), K(document, "pointermove", this._onTouchMove), K(document, "dragover", Ka), K(document, "mousemove", Ka), K(document, "touchmove", Ka); + q(document, "mousemove", this._onTouchMove), q(document, "touchmove", this._onTouchMove), q(document, "pointermove", this._onTouchMove), q(document, "dragover", Ka), q(document, "mousemove", Ka), q(document, "touchmove", Ka); }, _offUpEvents: function() { var e = this.el.ownerDocument; - K(e, "mouseup", this._onDrop), K(e, "touchend", this._onDrop), K(e, "pointerup", this._onDrop), K(e, "pointercancel", this._onDrop), K(e, "touchcancel", this._onDrop), K(document, "selectstart", this); + q(e, "mouseup", this._onDrop), q(e, "touchend", this._onDrop), q(e, "pointerup", this._onDrop), q(e, "pointercancel", this._onDrop), q(e, "touchcancel", this._onDrop), q(document, "selectstart", this); }, _onDrop: function(e) { var t = this.el, n = this.options; - if (da = Ri(J), pa = Ri(J, n.draggable), aa("drop", this, { evt: e }), Y = J && J.parentNode, da = Ri(J), pa = Ri(J, n.draggable), $.eventCanceled) { + if (da = Li(Y), pa = Li(Y, n.draggable), ia("drop", this, { evt: e }), oa = Y && Y.parentNode, da = Li(Y), pa = Li(Y, n.draggable), $.eventCanceled) { this._nulling(); return; } - ga = !1, ka = !1, Oa = !1, clearInterval(this._loopId), clearTimeout(this._dragStartTimer), ao(this.cloneId), ao(this._dragStartId), this.nativeDraggable && (K(document, "drop", this), K(t, "dragstart", this._onDragStart)), this._offMoveEvents(), this._offUpEvents(), Si && q(document.body, "user-select", ""), q(J, "transform", ""), e && (Ta && (e.cancelable && e.preventDefault(), !n.dropBubble && e.stopPropagation()), X && X.parentNode && X.parentNode.removeChild(X), (Z === Y || ha && ha.lastPutMode !== "clone") && Q && Q.parentNode && Q.parentNode.removeChild(Q), J && (this.nativeDraggable && K(J, "dragend", this), Xa(J), J.style["will-change"] = "", Ta && !ga && Ai(J, ha ? ha.options.ghostClass : this.options.ghostClass, !1), Ai(J, this.options.chosenClass, !1), oa({ + ga = !1, ka = !1, Oa = !1, clearInterval(this._loopId), clearTimeout(this._dragStartTimer), ao(this.cloneId), ao(this._dragStartId), this.nativeDraggable && (q(document, "drop", this), q(t, "dragstart", this._onDragStart)), this._offMoveEvents(), this._offUpEvents(), xi && J(document.body, "user-select", ""), J(Y, "transform", ""), e && (Ta && (e.cancelable && e.preventDefault(), !n.dropBubble && e.stopPropagation()), X && X.parentNode && X.parentNode.removeChild(X), (Z === oa || ha && ha.lastPutMode !== "clone") && Q && Q.parentNode && Q.parentNode.removeChild(Q), Y && (this.nativeDraggable && q(Y, "dragend", this), Xa(Y), Y.style["will-change"] = "", Ta && !ga && ki(Y, ha ? ha.options.ghostClass : this.options.ghostClass, !1), ki(Y, this.options.chosenClass, !1), aa({ sortable: this, name: "unchoose", - toEl: Y, + toEl: oa, newIndex: null, newDraggableIndex: null, originalEvent: e - }), Z === Y ? da !== ua && da >= 0 && (oa({ + }), Z === oa ? da !== ua && da >= 0 && (aa({ sortable: this, name: "update", - toEl: Y, + toEl: oa, originalEvent: e - }), oa({ + }), aa({ sortable: this, name: "sort", - toEl: Y, + toEl: oa, originalEvent: e - })) : (da >= 0 && (oa({ - rootEl: Y, + })) : (da >= 0 && (aa({ + rootEl: oa, name: "add", - toEl: Y, + toEl: oa, fromEl: Z, originalEvent: e - }), oa({ + }), aa({ sortable: this, name: "remove", - toEl: Y, + toEl: oa, originalEvent: e - }), oa({ - rootEl: Y, + }), aa({ + rootEl: oa, name: "sort", - toEl: Y, + toEl: oa, fromEl: Z, originalEvent: e - }), oa({ + }), aa({ sortable: this, name: "sort", - toEl: Y, + toEl: oa, originalEvent: e - })), ha && ha.save()), $.active && ((da == null || da === -1) && (da = ua, pa = fa), oa({ + })), ha && ha.save()), $.active && ((da == null || da === -1) && (da = ua, pa = fa), aa({ sortable: this, name: "end", - toEl: Y, + toEl: oa, originalEvent: e }), this.save()))), this._nulling(); }, _nulling: function() { - aa("nulling", this), Z = J = Y = X = sa = Q = ca = la = ya = ba = Ta = da = pa = ua = fa = Ea = Da = ha = ma = $.dragged = $.ghost = $.clone = $.active = null; + ia("nulling", this), Z = Y = oa = X = sa = Q = ca = la = ya = ba = Ta = da = pa = ua = fa = Ea = Da = ha = ma = $.dragged = $.ghost = $.clone = $.active = null; var e = this.el; Pa.forEach(function(t) { e.contains(t) && (t.checked = !0); @@ -3824,20 +3841,20 @@ $.prototype = { break; case "dragenter": case "dragover": - J && (this._onDragOver(e), Ja(e)); + Y && (this._onDragOver(e), Ja(e)); break; case "selectstart": e.preventDefault(); } }, toArray: function() { - for (var e = [], t, n = this.el.children, r = 0, i = n.length, a = this.options; r < i; r++) t = n[r], Oi(t, a.draggable, this.el, !1) && e.push(t.getAttribute(a.dataIdAttr) || no(t)); + for (var e = [], t, n = this.el.children, r = 0, i = n.length, a = this.options; r < i; r++) t = n[r], Di(t, a.draggable, this.el, !1) && e.push(t.getAttribute(a.dataIdAttr) || no(t)); return e; }, sort: function(e, t) { var n = {}, r = this.el; this.toArray().forEach(function(e, t) { var i = r.children[t]; - Oi(i, this.options.draggable, r, !1) && (n[e] = i); + Di(i, this.options.draggable, r, !1) && (n[e] = i); }, this), t && this.captureAnimationState(), e.forEach(function(e) { n[e] && (r.removeChild(n[e]), r.appendChild(n[e])); }), t && this.animateAll(); @@ -3847,25 +3864,25 @@ $.prototype = { e && e.set && e.set(this); }, closest: function(e, t) { - return Oi(e, t || this.options.draggable, this.el, !1); + return Di(e, t || this.options.draggable, this.el, !1); }, option: function(e, t) { var n = this.options; if (t === void 0) return n[e]; - var r = na.modifyOption(this, e, t); + var r = ta.modifyOption(this, e, t); n[e] = r === void 0 ? t : r, e === "group" && Ua(n); }, destroy: function() { - aa("destroy", this); + ia("destroy", this); var e = this.el; - e[Xi] = null, K(e, "mousedown", this._onTapStart), K(e, "touchstart", this._onTapStart), K(e, "pointerdown", this._onTapStart), this.nativeDraggable && (K(e, "dragover", this), K(e, "dragenter", this)), Array.prototype.forEach.call(e.querySelectorAll("[draggable]"), function(e) { + e[Yi] = null, q(e, "mousedown", this._onTapStart), q(e, "touchstart", this._onTapStart), q(e, "pointerdown", this._onTapStart), this.nativeDraggable && (q(e, "dragover", this), q(e, "dragenter", this)), Array.prototype.forEach.call(e.querySelectorAll("[draggable]"), function(e) { e.removeAttribute("draggable"); }), this._onDrop(), this._disableDelayedDragEvents(), va.splice(va.indexOf(this.el), 1), this.el = e = null; }, _hideClone: function() { if (!la) { - if (aa("hideClone", this), $.eventCanceled) return; - q(Q, "display", "none"), this.options.removeCloneOnHide && Q.parentNode && Q.parentNode.removeChild(Q), la = !0; + if (ia("hideClone", this), $.eventCanceled) return; + J(Q, "display", "none"), this.options.removeCloneOnHide && Q.parentNode && Q.parentNode.removeChild(Q), la = !0; } }, _showClone: function(e) { @@ -3874,8 +3891,8 @@ $.prototype = { return; } if (la) { - if (aa("showClone", this), $.eventCanceled) return; - J.parentNode == Z && !this.options.group.revertClone ? Z.insertBefore(Q, J) : sa ? Z.insertBefore(Q, sa) : Z.appendChild(Q), this.options.group.revertClone && this.animate(J, Q), q(Q, "display", ""), la = !1; + if (ia("showClone", this), $.eventCanceled) return; + Y.parentNode == Z && !this.options.group.revertClone ? Z.insertBefore(Q, Y) : sa ? Z.insertBefore(Q, sa) : Z.appendChild(Q), this.options.group.revertClone && this.animate(Y, Q), J(Q, "display", ""), la = !1; } } }; @@ -3883,11 +3900,11 @@ function Ja(e) { e.dataTransfer && (e.dataTransfer.dropEffect = "move"), e.cancelable && e.preventDefault(); } function Ya(e, t, n, r, i, a, o, s) { - var c, l = e[Xi], u = l.options.onMove, d; - return window.CustomEvent && !yi && !bi ? c = new CustomEvent("move", { + var c, l = e[Yi], u = l.options.onMove, d; + return window.CustomEvent && !vi && !yi ? c = new CustomEvent("move", { bubbles: !0, cancelable: !0 - }) : (c = document.createEvent("Event"), c.initEvent("move", !0, !0)), c.to = t, c.from = e, c.dragged = n, c.draggedRect = r, c.related = i || t, c.relatedRect = a || Pi(t), c.willInsertAfter = s, c.originalEvent = o, e.dispatchEvent(c), u && (d = u.call(l, c, o)), d; + }) : (c = document.createEvent("Event"), c.initEvent("move", !0, !0)), c.to = t, c.from = e, c.dragged = n, c.draggedRect = r, c.related = i || t, c.relatedRect = a || Ni(t), c.willInsertAfter = s, c.originalEvent = o, e.dispatchEvent(c), u && (d = u.call(l, c, o)), d; } function Xa(e) { e.draggable = !1; @@ -3896,11 +3913,11 @@ function Za() { Na = !1; } function Qa(e, t, n) { - var r = Pi(Ii(n.el, 0, n.options, !0)), i = Yi(n.el, n.options, X), a = 10; + var r = Ni(Fi(n.el, 0, n.options, !0)), i = Ji(n.el, n.options, X), a = 10; return t ? e.clientX < i.left - a || e.clientY < r.top && e.clientX < r.right : e.clientY < i.top - a || e.clientY < r.bottom && e.clientX < r.left; } function $a(e, t, n) { - var r = Pi(Li(n.el, n.options.draggable)), i = Yi(n.el, n.options, X), a = 10; + var r = Ni(Ii(n.el, n.options.draggable)), i = Ji(n.el, n.options, X), a = 10; return t ? e.clientX > i.right + a || e.clientY > r.bottom && e.clientX > r.left : e.clientY > i.bottom + a || e.clientX > r.right && e.clientY > r.top; } function eo(e, t, n, r, i, a, o, s) { @@ -3914,7 +3931,7 @@ function eo(e, t, n, r, i, a, o, s) { return f ||= o, f && (c < u + l * a / 2 || c > d - l * a / 2) ? c > u + l / 2 ? 1 : -1 : 0; } function to(e) { - return Ri(J) < Ri(e) ? 1 : -1; + return Li(Y) < Li(e) ? 1 : -1; } function no(e) { for (var t = e.tagName + e.className + e.src + e.href + e.textContent, n = t.length, r = 0; n--;) r += t.charCodeAt(n); @@ -3933,38 +3950,38 @@ function io(e) { function ao(e) { return clearTimeout(e); } -Fa && G(document, "touchmove", function(e) { +Fa && K(document, "touchmove", function(e) { ($.active || ga) && e.cancelable && e.preventDefault(); }), $.utils = { - on: G, - off: K, - css: q, - find: Mi, + on: K, + off: q, + css: J, + find: ji, is: function(e, t) { - return !!Oi(e, t, e, !1); + return !!Di(e, t, e, !1); }, - extend: Hi, - throttle: Gi, - closest: Oi, - toggleClass: Ai, - clone: Ji, - index: Ri, + extend: Vi, + throttle: Wi, + closest: Di, + toggleClass: ki, + clone: qi, + index: Li, nextTick: io, cancelNextTick: ao, detectDirection: Ba, - getChild: Ii, - expando: Xi + getChild: Fi, + expando: Yi }, $.get = function(e) { - return e[Xi]; + return e[Yi]; }, $.mount = function() { var e = [...arguments]; e[0].constructor === Array && (e = e[0]), e.forEach(function(e) { if (!e.prototype || !e.prototype.constructor) throw `Sortable: Mounted plugin must be a constructor function, not ${{}.toString.call(e)}`; - e.utils && ($.utils = di(di({}, $.utils), e.utils)), na.mount(e); + e.utils && ($.utils = ui(ui({}, $.utils), e.utils)), ta.mount(e); }); }, $.create = function(e, t) { return new $(e, t); -}, $.version = _i; +}, $.version = gi; var oo = [], so, co, lo = !1, uo, fo, po, mo; function ho() { function e() { @@ -3979,14 +3996,14 @@ function ho() { return e.prototype = { dragStarted: function(e) { var t = e.originalEvent; - this.sortable.nativeDraggable ? G(document, "dragover", this._handleAutoScroll) : this.options.supportPointer ? G(document, "pointermove", this._handleFallbackAutoScroll) : t.touches ? G(document, "touchmove", this._handleFallbackAutoScroll) : G(document, "mousemove", this._handleFallbackAutoScroll); + this.sortable.nativeDraggable ? K(document, "dragover", this._handleAutoScroll) : this.options.supportPointer ? K(document, "pointermove", this._handleFallbackAutoScroll) : t.touches ? K(document, "touchmove", this._handleFallbackAutoScroll) : K(document, "mousemove", this._handleFallbackAutoScroll); }, dragOverCompleted: function(e) { var t = e.originalEvent; !this.options.dragOverBubble && !t.rootEl && this._handleAutoScroll(t); }, drop: function() { - this.sortable.nativeDraggable ? K(document, "dragover", this._handleAutoScroll) : (K(document, "pointermove", this._handleFallbackAutoScroll), K(document, "touchmove", this._handleFallbackAutoScroll), K(document, "mousemove", this._handleFallbackAutoScroll)), _o(), go(), Ki(); + this.sortable.nativeDraggable ? q(document, "dragover", this._handleAutoScroll) : (q(document, "pointermove", this._handleFallbackAutoScroll), q(document, "touchmove", this._handleFallbackAutoScroll), q(document, "mousemove", this._handleFallbackAutoScroll)), _o(), go(), Gi(); }, nulling: function() { po = co = so = lo = mo = uo = fo = null, oo.length = 0; @@ -3996,22 +4013,22 @@ function ho() { }, _handleAutoScroll: function(e, t) { var n = this, r = (e.touches ? e.touches[0] : e).clientX, i = (e.touches ? e.touches[0] : e).clientY, a = document.elementFromPoint(r, i); - if (po = e, t || this.options.forceAutoScrollFallback || bi || yi || Si) { + if (po = e, t || this.options.forceAutoScrollFallback || yi || vi || xi) { vo(e, this.options, a, t); - var o = Vi(a, !0); + var o = Bi(a, !0); lo && (!mo || r !== uo || i !== fo) && (mo && _o(), mo = setInterval(function() { - var a = Vi(document.elementFromPoint(r, i), !0); + var a = Bi(document.elementFromPoint(r, i), !0); a !== o && (o = a, go()), vo(e, n.options, a, t); }, 10), uo = r, fo = i); } else { - if (!this.options.bubbleScroll || Vi(a, !0) === Ni()) { + if (!this.options.bubbleScroll || Bi(a, !0) === Mi()) { go(); return; } - vo(e, this.options, Vi(a, !1), !1); + vo(e, this.options, Bi(a, !1), !1); } } - }, li(e, { + }, ci(e, { pluginName: "scroll", initializeByDefault: !0 }); @@ -4024,22 +4041,22 @@ function go() { function _o() { clearInterval(mo); } -var vo = Gi(function(e, t, n, r) { +var vo = Wi(function(e, t, n, r) { if (t.scroll) { - var i = (e.touches ? e.touches[0] : e).clientX, a = (e.touches ? e.touches[0] : e).clientY, o = t.scrollSensitivity, s = t.scrollSpeed, c = Ni(), l = !1, u; - co !== n && (co = n, go(), so = t.scroll, u = t.scrollFn, so === !0 && (so = Vi(n, !0))); + var i = (e.touches ? e.touches[0] : e).clientX, a = (e.touches ? e.touches[0] : e).clientY, o = t.scrollSensitivity, s = t.scrollSpeed, c = Mi(), l = !1, u; + co !== n && (co = n, go(), so = t.scroll, u = t.scrollFn, so === !0 && (so = Bi(n, !0))); var d = 0, f = so; do { - var p = f, m = Pi(p), h = m.top, g = m.bottom, _ = m.left, v = m.right, y = m.width, b = m.height, x = void 0, S = void 0, C = p.scrollWidth, w = p.scrollHeight, T = q(p), E = p.scrollLeft, D = p.scrollTop; + var p = f, m = Ni(p), h = m.top, g = m.bottom, _ = m.left, v = m.right, y = m.width, b = m.height, x = void 0, S = void 0, C = p.scrollWidth, w = p.scrollHeight, T = J(p), E = p.scrollLeft, D = p.scrollTop; p === c ? (x = y < C && (T.overflowX === "auto" || T.overflowX === "scroll" || T.overflowX === "visible"), S = b < w && (T.overflowY === "auto" || T.overflowY === "scroll" || T.overflowY === "visible")) : (x = y < C && (T.overflowX === "auto" || T.overflowX === "scroll"), S = b < w && (T.overflowY === "auto" || T.overflowY === "scroll")); var O = x && (Math.abs(v - i) <= o && E + y < C) - (Math.abs(_ - i) <= o && !!E), k = S && (Math.abs(g - a) <= o && D + b < w) - (Math.abs(h - a) <= o && !!D); if (!oo[d]) for (var A = 0; A <= d; A++) oo[A] || (oo[A] = {}); (oo[d].vx != O || oo[d].vy != k || oo[d].el !== p) && (oo[d].el = p, oo[d].vx = O, oo[d].vy = k, clearInterval(oo[d].pid), (O != 0 || k != 0) && (l = !0, oo[d].pid = setInterval(function() { r && this.layer === 0 && $.active._onTouchMove(po); var t = oo[this.layer].vy ? oo[this.layer].vy * s : 0, n = oo[this.layer].vx ? oo[this.layer].vx * s : 0; - (typeof u != "function" || u.call($.dragged.parentNode[Xi], n, t, e, po, oo[this.layer].el) === "continue") && qi(oo[this.layer].el, n, t); + (typeof u != "function" || u.call($.dragged.parentNode[Yi], n, t, e, po, oo[this.layer].el) === "continue") && Ki(oo[this.layer].el, n, t); }.bind({ layer: d }), 24))), d++; - } while (t.bubbleScroll && f !== c && (f = Vi(f, !1))); + } while (t.bubbleScroll && f !== c && (f = Bi(f, !1))); lo = l; } }, 30), yo = function(e) { @@ -4064,11 +4081,11 @@ bo.prototype = { onSpill: function(e) { var t = e.dragEl, n = e.putSortable; this.sortable.captureAnimationState(), n && n.captureAnimationState(); - var r = Ii(this.sortable.el, this.startIndex, this.options); + var r = Fi(this.sortable.el, this.startIndex, this.options); r ? this.sortable.el.insertBefore(t, r) : this.sortable.el.appendChild(t), this.sortable.animateAll(), n && n.animateAll(); }, drop: yo -}, li(bo, { pluginName: "revertOnSpill" }); +}, ci(bo, { pluginName: "revertOnSpill" }); function xo() {} xo.prototype = { onSpill: function(e) { @@ -4076,7 +4093,7 @@ xo.prototype = { n.captureAnimationState(), t.parentNode && t.parentNode.removeChild(t), n.animateAll(); }, drop: yo -}, li(xo, { pluginName: "removeOnSpill" }), $.mount(new ho()), $.mount(xo, bo); +}, ci(xo, { pluginName: "removeOnSpill" }), $.mount(new ho()), $.mount(xo, bo); //#endregion //#region resources/js/api/board.ts var So = { @@ -4164,7 +4181,7 @@ var Fo = [ "deleted" ], setup(e, { emit: t }) { - let l = e, u = t, d = H(), f = _({ + let l = e, u = t, d = de(), f = _({ name: "", description: "", estimateHours: "", @@ -4194,14 +4211,14 @@ var Fo = [ } function L() { let e = l.task; - f.name = e?.name ?? "", f.description = e?.description ?? "", f.estimateHours = oe(e?.estimated_minutes ?? null), f.rate = ie(e?.rate ?? null), f.dueDate = e?.due_date ?? "", f.billable = e?.billable ?? !0; + f.name = e?.name ?? "", f.description = e?.description ?? "", f.estimateHours = ie(e?.estimated_minutes ?? null), f.rate = ne(e?.rate ?? null), f.dueDate = e?.due_date ?? "", f.billable = e?.billable ?? !0; let t = l.statuses.find((e) => e.is_default) ?? l.statuses[0], n = e?.task_status_id ?? l.defaults?.task_status_id ?? t?.id ?? null; p.value = I(N.value, n), m.value = e?.project_id ?? l.defaults?.project_id ?? null, h.value = I(P.value, e?.assignee_id ?? null), y.value = e?.priority ? F.value[Fo.indexOf(e.priority)] ?? null : null, C.value = e?.customer_id ?? null, w.value = {}; } - function ee(e) { - f.dueDate = e ? le(e) : ""; + function R(e) { + f.dueDate = e ? se(e) : ""; } - function R() { + function z() { let e = p.value?.id ?? null; if (e === null) return null; let t = y.value === null ? null : Fo[y.value.id]; @@ -4214,18 +4231,18 @@ var Fo = [ assignee_id: h.value?.id ?? null, priority: t, due_date: f.dueDate || null, - estimated_minutes: se(f.estimateHours), + estimated_minutes: ae(f.estimateHours), billable: f.billable, - rate: ae(f.rate) + rate: re(f.rate) }; } - async function z() { + async function B() { if (D.value) return; if (f.name.trim() === "") { w.value = { name: d("tasks_projects.tasks.name_required") }; return; } - let e = R(); + let e = z(); if (e === null) { l.notify("error", d("tasks_projects.task_statuses.none")); return; @@ -4235,27 +4252,27 @@ var Fo = [ let t = l.task, n = t ? await Do(l.client, t.id, e) : await Eo(l.client, e); u("saved", n); } catch (e) { - w.value = re(e), l.notify("error", V(e, d("tasks_projects.tasks.save_failed"))); + w.value = te(e), l.notify("error", U(e, d("tasks_projects.tasks.save_failed"))); } finally { D.value = !1; } } - async function te() { + async function V() { let e = l.task; if (!(e === null || O.value) && window.confirm(d("tasks_projects.tasks.delete_confirm", { name: e.name }))) { O.value = !0; try { await Oo(l.client, e.id), u("deleted", e); } catch (e) { - l.notify("error", V(e, d("tasks_projects.tasks.delete_failed"))); + l.notify("error", U(e, d("tasks_projects.tasks.delete_failed"))); } finally { O.value = !1; } } } return (t, n) => { - let l = b("BaseIcon"), m = b("BaseInput"), _ = b("BaseInputGroup"), v = b("BaseSelectInput"), T = b("BaseDatePicker"), I = b("BaseInputGrid"), L = b("BaseSwitch"), R = b("BaseTextarea"), B = b("BaseButton"), ne = b("BaseModal"); - return g(), r(ne, { + let l = b("BaseIcon"), m = b("BaseInput"), _ = b("BaseInputGroup"), v = b("BaseSelectInput"), T = b("BaseDatePicker"), I = b("BaseInputGrid"), L = b("BaseSwitch"), z = b("BaseTextarea"), H = b("BaseButton"), ee = b("BaseModal"); + return g(), r(ee, { show: e.show, onClose: n[11] ||= (e) => u("close") }, { @@ -4264,7 +4281,7 @@ var Fo = [ class: "h-6 w-6 cursor-pointer text-subtle hover:text-body", onClick: n[0] ||= (e) => u("close") })])]), - default: E(() => [o("form", { onSubmit: k(z, ["prevent"]) }, [o("div", Ro, [ + default: E(() => [o("form", { onSubmit: k(B, ["prevent"]) }, [o("div", Ro, [ c(_, { label: S(d)("tasks_projects.tasks.fields.name"), error: w.value.name, @@ -4367,7 +4384,7 @@ var Fo = [ }, { default: E(() => [c(T, { "model-value": f.dueDate, - "onUpdate:modelValue": ee + "onUpdate:modelValue": R }, null, 8, ["model-value"])]), _: 1 }, 8, ["label", "error"]), @@ -4422,7 +4439,7 @@ var Fo = [ label: S(d)("tasks_projects.tasks.fields.description"), error: w.value.description }, { - default: E(() => [c(R, { + default: E(() => [c(z, { modelValue: f.description, "onUpdate:modelValue": n[9] ||= (e) => f.description = e, row: 3, @@ -4430,24 +4447,24 @@ var Fo = [ }, null, 8, ["modelValue", "invalid"])]), _: 1 }, 8, ["label", "error"]) - ]), o("div", zo, [j.value ? (g(), r(B, { + ]), o("div", zo, [j.value ? (g(), r(H, { key: 0, type: "button", variant: "danger", loading: O.value, disabled: O.value, - onClick: te + onClick: V }, { default: E(() => [s(x(S(d)("tasks_projects.general.delete")), 1)]), _: 1 - }, 8, ["loading", "disabled"])) : (g(), a("span", Bo)), o("div", Vo, [c(B, { + }, 8, ["loading", "disabled"])) : (g(), a("span", Bo)), o("div", Vo, [c(H, { type: "button", variant: "primary-outline", onClick: n[10] ||= (e) => u("close") }, { default: E(() => [s(x(S(d)("tasks_projects.general.cancel")), 1)]), _: 1 - }), c(B, { + }), c(H, { type: "submit", variant: "primary", loading: D.value, @@ -4486,7 +4503,7 @@ var Fo = [ notify: { type: Function } }, setup(t) { - let l = t, u = H(), d = v([]), _ = v([]), C = v([]), w = v(!0), D = v(null), O = v(null), k = v(!1), A = v(null), j = v({}), M = { + let l = t, u = de(), d = v([]), _ = v([]), C = v([]), w = v(!0), D = v(null), O = v(null), k = v(!1), A = v(null), j = v({}), M = { LOW: "bg-surface-tertiary text-muted", NORMAL: "bg-primary-50 text-primary-500", HIGH: "bg-alert-warning-bg text-alert-warning-text", @@ -4494,19 +4511,19 @@ var Fo = [ }, N = /* @__PURE__ */ new Map(), P = /* @__PURE__ */ new Map(), I = !1, L = n(() => C.value.map((e) => ({ id: e.id, label: e.name - }))), ee = n(() => _.value.map((e) => ({ + }))), R = n(() => _.value.map((e) => ({ id: e.id, label: e.name - }))), R = n(() => d.value.map((e) => e.status)), z = n(() => !w.value && d.value.length === 0); + }))), z = n(() => d.value.map((e) => e.status)), B = n(() => !w.value && d.value.length === 0); T([D, O], () => { - ne(); + ee(); }), h(() => { - B(), ne(); + H(), ee(); }), m(() => { for (let e of N.values()) e.destroy(); N.clear(), P.clear(); }); - async function B() { + async function H() { try { let e = await F(l.client, { limit: 100, @@ -4515,26 +4532,26 @@ var Fo = [ }); C.value = e.data; } catch (e) { - l.notify("error", V(e, u("tasks_projects.tasks.projects_failed"))); + l.notify("error", U(e, u("tasks_projects.tasks.projects_failed"))); } try { - _.value = await te(l.client); + _.value = await V(l.client); } catch (e) { - l.notify("error", V(e, u("tasks_projects.tasks.members_failed"))); + l.notify("error", U(e, u("tasks_projects.tasks.members_failed"))); } } - async function ne() { + async function ee() { let e = {}; D.value && (e.project_id = D.value.id), O.value && (e.assignee_id = O.value.id), w.value = !0; try { - d.value = await Co(l.client, e), d.value.some((e) => e.tasks.some((e) => e.customer_id !== null)) && Ce(l.client); + d.value = await Co(l.client, e), d.value.some((e) => e.tasks.some((e) => e.customer_id !== null)) && Se(l.client); } catch (e) { - l.notify("error", V(e, u("tasks_projects.board.load_failed"))); + l.notify("error", U(e, u("tasks_projects.board.load_failed"))); } finally { w.value = !1; } } - function re(e, t) { + function te(e, t) { let n = t instanceof HTMLElement ? t : null; P.get(e) !== n && (N.get(e)?.destroy(), N.delete(e), P.delete(e), n !== null && (P.set(e, n), N.set(e, $.create(n, { group: "tasks", @@ -4545,23 +4562,23 @@ var Fo = [ I = !0; }, onEnd: (e) => { - oe(e), setTimeout(() => { + ie(e), setTimeout(() => { I = !1; }); } })))); } - function ie(e) { + function ne(e) { let t = e.item, n = e.oldIndex ?? 0; t.parentNode?.removeChild(t), e.from.insertBefore(t, e.from.children[n] ?? null); } - function ae(e) { + function re(e) { return d.value.find((t) => t.status.id === e); } - async function oe(e) { + async function ie(e) { let t = Number(e.from.dataset.statusId), n = Number(e.to.dataset.statusId), r = e.oldIndex ?? 0, i = e.newIndex ?? 0; - if (ie(e), Number.isNaN(t) || Number.isNaN(n) || t === n && r === i) return; - let a = ae(t), o = ae(n); + if (ne(e), Number.isNaN(t) || Number.isNaN(n) || t === n && r === i) return; + let a = re(t), o = re(n); if (!a || !o) return; let s = { from: [...a.tasks], @@ -4584,53 +4601,53 @@ var Fo = [ status: o.status.name })); } catch (e) { - a.tasks = s.from, o.tasks = s.to, l.notify("error", V(e, u("tasks_projects.board.move_failed"))); + a.tasks = s.from, o.tasks = s.to, l.notify("error", U(e, u("tasks_projects.board.move_failed"))); } } - function se(e) { + function ae(e) { A.value = null, j.value = { task_status_id: e.id, project_id: D.value?.id ?? null }, k.value = !0; } - function le(e) { + function se(e) { I || (A.value = e, j.value = {}, k.value = !0); } - function ue(e) { + function ce(e) { let t = A.value ? u("tasks_projects.tasks.updated", { name: e.name }) : u("tasks_projects.tasks.created", { name: e.name }); - k.value = !1, A.value = null, l.notify("success", t), ne(); + k.value = !1, A.value = null, l.notify("success", t), ee(); } - function pe(e) { - k.value = !1, A.value = null, l.notify("success", u("tasks_projects.tasks.deleted", { name: e.name })), ne(); + function fe(e) { + k.value = !1, A.value = null, l.notify("success", u("tasks_projects.tasks.deleted", { name: e.name })), ee(); } - function me() { + function pe() { D.value = null, O.value = null; } - function he(e) { + function me(e) { if (e.project_id === null) return null; let t = C.value.find((t) => t.id === e.project_id); return t?.identifier || t?.name || null; } - function ge(e) { - return [C.value.find((t) => t.id === e.project_id)?.name, Se(e.customer_id)].filter(Boolean).join(" · "); + function he(e) { + return [C.value.find((t) => t.id === e.project_id)?.name, xe(e.customer_id)].filter(Boolean).join(" · "); } - function _e(e) { + function ge(e) { if (e.assignee_id === null) return null; let t = _.value.find((t) => t.id === e.assignee_id); - return t ? de(t.name) : `#${e.assignee_id}`; + return t ? le(t.name) : `#${e.assignee_id}`; } - function ve(e) { + function _e(e) { return e.assignee_id === null ? u("tasks_projects.tasks.unassigned") : _.value.find((t) => t.id === e.assignee_id)?.name ?? `#${e.assignee_id}`; } - function ye(e) { + function ve(e) { return u(`tasks_projects.tasks.priority.${e.toLowerCase()}`); } - function be(e) { + function ye(e) { return M[e]; } return (n, l) => { - let m = b("BaseBreadcrumbItem"), h = b("BaseBreadcrumb"), v = b("BaseIcon"), C = b("BaseButton"), T = b("router-link"), M = b("BasePageHeader"), N = b("BaseSelectInput"), P = b("BaseInputGroup"), F = b("BaseFilterWrapper"), I = b("BaseSpinner"), te = b("BaseEmptyPlaceholder"), B = b("BasePage"); - return g(), r(B, null, { + let m = b("BaseBreadcrumbItem"), h = b("BaseBreadcrumb"), v = b("BaseIcon"), C = b("BaseButton"), T = b("router-link"), M = b("BasePageHeader"), N = b("BaseSelectInput"), P = b("BaseInputGroup"), F = b("BaseFilterWrapper"), I = b("BaseSpinner"), V = b("BaseEmptyPlaceholder"), H = b("BasePage"); + return g(), r(H, null, { default: E(() => [ c(M, { title: S(u)("tasks_projects.board.title") }, { actions: E(() => [o("div", Uo, [c(T, { to: "/admin/modules/tasks-projects" }, { @@ -4667,7 +4684,7 @@ var Fo = [ c(F, { show: !0, class: "mt-4", - onClear: me + onClear: pe }, { default: E(() => [c(P, { label: S(u)("tasks_projects.board.filters.project"), @@ -4692,7 +4709,7 @@ var Fo = [ default: E(() => [c(N, { modelValue: O.value, "onUpdate:modelValue": l[1] ||= (e) => O.value = e, - options: ee.value, + options: R.value, placeholder: S(u)("tasks_projects.board.filters.all_assignees"), "label-key": "label" }, null, 8, [ @@ -4704,7 +4721,7 @@ var Fo = [ }, 8, ["label"])]), _: 1 }), - w.value && d.value.length === 0 ? (g(), a("div", Wo, [c(I, { class: "h-8 w-8 text-primary-500" })])) : z.value ? (g(), r(te, { + w.value && d.value.length === 0 ? (g(), a("div", Wo, [c(I, { class: "h-8 w-8 text-primary-500" })])) : B.value ? (g(), r(V, { key: 1, title: S(u)("tasks_projects.task_statuses.none"), description: S(u)("tasks_projects.tasks.empty_description") @@ -4730,33 +4747,33 @@ var Fo = [ class: "rounded-md p-1 text-subtle hover:bg-hover hover:text-body", "aria-label": S(u)("tasks_projects.tasks.new_task"), title: S(u)("tasks_projects.tasks.new_task"), - onClick: (e) => se(t.status) + onClick: (e) => ae(t.status) }, [c(v, { name: "PlusIcon", class: "h-4 w-4" })], 8, Xo)]), o("div", { ref_for: !0, - ref: (e) => re(t.status.id, e), + ref: (e) => te(t.status.id, e), "data-status-id": t.status.id, class: "min-h-[80px] space-y-2 px-3 pt-3" }, [(g(!0), a(e, null, y(t.tasks, (e) => (g(), a("article", { key: e.id, "data-task-id": e.id, class: "cursor-pointer rounded-lg border border-line-default bg-surface p-3 shadow-sm hover:bg-hover", - onClick: (t) => le(e) + onClick: (t) => se(e) }, [ o("div", $o, [o("p", es, x(e.name), 1), e.priority ? (g(), a("span", { key: 0, - class: f(["shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium", be(e.priority)]) - }, x(ye(e.priority)), 3)) : i("", !0)]), + class: f(["shrink-0 rounded-full px-2 py-0.5 text-[11px] font-medium", ye(e.priority)]) + }, x(ve(e.priority)), 3)) : i("", !0)]), o("div", ts, [ o("span", null, "#" + x(e.number), 1), - he(e) ? (g(), a("span", { + me(e) ? (g(), a("span", { key: 0, class: "rounded-sm bg-surface-tertiary px-1.5 py-0.5 text-[11px] text-body", - title: ge(e) - }, x(he(e)), 9, ns)) : i("", !0), + title: he(e) + }, x(me(e)), 9, ns)) : i("", !0), e.billable ? (g(), a("span", rs, [c(v, { name: "CurrencyDollarIcon", class: "mr-0.5 h-3.5 w-3.5" @@ -4764,18 +4781,18 @@ var Fo = [ ]), o("div", is, [e.due_date ? (g(), a("span", { key: 0, - class: f(["text-xs", S(fe)(e.due_date) && !e.closed_at ? "font-medium text-status-red" : "text-muted"]) - }, x(S(ce)(e.due_date)), 3)) : (g(), a("span", as, "-")), _e(e) ? (g(), a("span", { + class: f(["text-xs", S(ue)(e.due_date) && !e.closed_at ? "font-medium text-status-red" : "text-muted"]) + }, x(S(oe)(e.due_date)), 3)) : (g(), a("span", as, "-")), ge(e) ? (g(), a("span", { key: 2, class: "flex h-6 w-6 items-center justify-center rounded-full bg-primary-50 text-[11px] font-semibold text-primary-500", - title: ve(e) - }, x(_e(e)), 9, os)) : i("", !0)]) + title: _e(e) + }, x(ge(e)), 9, os)) : i("", !0)]) ], 8, Qo))), 128))], 8, Zo), t.tasks.length === 0 ? (g(), a("p", ss, x(S(u)("tasks_projects.board.empty_column")), 1)) : i("", !0), o("div", cs, [o("button", { type: "button", class: "w-full rounded-md border border-dashed border-line-default py-1.5 text-xs text-muted hover:bg-hover hover:text-body", - onClick: (e) => se(t.status) + onClick: (e) => ae(t.status) }, " + " + x(S(u)("tasks_projects.tasks.new_task")), 9, ls)]) ]))), 128))])), c(Ho, { @@ -4783,13 +4800,13 @@ var Fo = [ client: t.client, notify: t.notify, task: A.value, - statuses: R.value, + statuses: z.value, members: _.value, projects: L.value, defaults: j.value, onClose: l[2] ||= (e) => k.value = !1, - onSaved: ue, - onDeleted: pe + onSaved: ce, + onDeleted: fe }, null, 8, [ "show", "client", @@ -4830,7 +4847,7 @@ var Fo = [ router: {} }, setup(t) { - let l = t, u = H(), d = v(null), p = v(!0), m = v(!1), _ = v(!1), C = n(() => Number(l.id)), w = n(() => [ + let l = t, u = de(), d = v(null), p = v(!0), m = v(!1), _ = v(!1), C = n(() => Number(l.id)), w = n(() => [ { id: "overview", label: u("tasks_projects.project.tabs.overview"), @@ -4851,7 +4868,7 @@ var Fo = [ label: u("tasks_projects.project.tabs.members"), name: `${Ss}.members` } - ]), D = n(() => String(l.router.currentRoute.value.name ?? "")), O = n(() => d.value?.name ?? u("tasks_projects.projects.title")), k = n(() => Se(d.value?.customer_id ?? null)); + ]), D = n(() => String(l.router.currentRoute.value.name ?? "")), O = n(() => d.value?.name ?? u("tasks_projects.projects.title")), k = n(() => xe(d.value?.customer_id ?? null)); T(C, () => { j(); }), T(D, (e) => A(e)), h(() => { @@ -4866,9 +4883,9 @@ var Fo = [ async function j() { p.value = !0; try { - d.value = await Ao(l.client, C.value), typeof d.value?.customer_id == "number" && await Ce(l.client); + d.value = await Ao(l.client, C.value), typeof d.value?.customer_id == "number" && await Se(l.client); } catch (e) { - l.notify("error", V(e, u("tasks_projects.project.load_failed"))); + l.notify("error", U(e, u("tasks_projects.project.load_failed"))); } finally { p.value = !1; } @@ -4890,9 +4907,9 @@ var Fo = [ if (!(e === null || m.value)) { m.value = !0; try { - e.status === "ARCHIVED" ? (await R(l.client, e.id), l.notify("success", u("tasks_projects.projects.unarchived", { name: e.name }))) : (await ee(l.client, e.id), l.notify("success", u("tasks_projects.projects.archived", { name: e.name }))), await j(); + e.status === "ARCHIVED" ? (await z(l.client, e.id), l.notify("success", u("tasks_projects.projects.unarchived", { name: e.name }))) : (await R(l.client, e.id), l.notify("success", u("tasks_projects.projects.archived", { name: e.name }))), await j(); } catch (e) { - l.notify("error", V(e, u("tasks_projects.projects.save_failed"))); + l.notify("error", U(e, u("tasks_projects.projects.save_failed"))); } finally { m.value = !1; } @@ -4905,10 +4922,10 @@ var Fo = [ return u(e === "ACTIVE" ? "tasks_projects.projects.status.active" : "tasks_projects.projects.status.archived"); } return (n, l) => { - let h = b("BaseBreadcrumbItem"), v = b("BaseBreadcrumb"), C = b("BaseBadge"), T = b("BaseIcon"), D = b("BaseButton"), A = b("router-link"), ee = b("BasePageHeader"), R = b("BaseSpinner"), z = b("router-view"), te = b("BasePage"); - return g(), r(te, null, { + let h = b("BaseBreadcrumbItem"), v = b("BaseBreadcrumb"), C = b("BaseBadge"), T = b("BaseIcon"), D = b("BaseButton"), A = b("router-link"), R = b("BasePageHeader"), z = b("BaseSpinner"), B = b("router-view"), V = b("BasePage"); + return g(), r(V, null, { default: E(() => [ - c(ee, { title: O.value }, { + c(R, { title: O.value }, { actions: E(() => [o("div", vs, [ c(A, { to: "/admin/modules/tasks-projects/board" }, { default: E(() => [c(D, { variant: "white" }, { @@ -4968,7 +4985,7 @@ var Fo = [ }, 8, ["class"]), d.value.identifier ? (g(), a("span", fs, x(d.value.identifier), 1)) : i("", !0), d.value.customer_id ? (g(), a("span", ps, [s(x(S(u)("tasks_projects.project.customer")) + ": ", 1), o("span", ms, x(k.value), 1)])) : (g(), a("span", hs, x(S(u)("tasks_projects.projects.internal")), 1)), - d.value.due_date ? (g(), a("span", gs, [s(x(S(u)("tasks_projects.project.due_date")) + ": ", 1), o("span", _s, x(S(ce)(d.value.due_date)), 1)])) : i("", !0) + d.value.due_date ? (g(), a("span", gs, [s(x(S(u)("tasks_projects.project.due_date")) + ": ", 1), o("span", _s, x(S(oe)(d.value.due_date)), 1)])) : i("", !0) ])) : i("", !0)]), _: 1 }, 8, ["title"]), @@ -4985,12 +5002,12 @@ var Fo = [ }, x(e.label), 11, bs)]), _: 2 }, 1032, ["to"]))), 128))]), - p.value && d.value === null ? (g(), a("div", xs, [c(R, { class: "h-8 w-8 text-primary-500" })])) : (g(), r(z, { + p.value && d.value === null ? (g(), a("div", xs, [c(z, { class: "h-8 w-8 text-primary-500" })])) : (g(), r(B, { key: 1, project: d.value, onRefresh: j }, null, 8, ["project"])), - c(ve, { + c(_e, { show: _.value, client: t.client, notify: t.notify, @@ -5032,7 +5049,7 @@ var Fo = [ project: {} }, setup(t) { - let l = t, u = H(), d = v([]), p = v([]), m = v(!0), _ = v(!1), C = v(null), w = v(null), T = v(""), D = v({}), O = n(() => l.project?.id ?? Number(l.id)), k = n(() => p.value.filter((e) => !d.value.some((t) => t.user_id === e.id)).map((e) => ({ + let l = t, u = de(), d = v([]), p = v([]), m = v(!0), _ = v(!1), C = v(null), w = v(null), T = v(""), D = v({}), O = n(() => l.project?.id ?? Number(l.id)), k = n(() => p.value.filter((e) => !d.value.some((t) => t.user_id === e.id)).map((e) => ({ id: e.id, label: e.name }))); @@ -5042,14 +5059,14 @@ var Fo = [ async function A() { m.value = !0; try { - p.value = await te(l.client); + p.value = await V(l.client); } catch (e) { - l.notify("error", V(e, u("tasks_projects.tasks.members_failed"))); + l.notify("error", U(e, u("tasks_projects.tasks.members_failed"))); } try { d.value = await jo(l.client, O.value); } catch (e) { - l.notify("error", V(e, u("tasks_projects.project.members.load_failed"))); + l.notify("error", U(e, u("tasks_projects.project.members.load_failed"))); } finally { m.value = !1; } @@ -5064,10 +5081,10 @@ var Fo = [ try { await Mo(l.client, O.value, { user_id: e.id, - rate: ae(T.value) + rate: re(T.value) }), l.notify("success", u("tasks_projects.project.members.attached", { name: e.label })), w.value = null, T.value = "", await A(); } catch (e) { - D.value = re(e), l.notify("error", V(e, u("tasks_projects.project.members.attach_failed"))); + D.value = te(e), l.notify("error", U(e, u("tasks_projects.project.members.attach_failed"))); } finally { _.value = !1; } @@ -5080,7 +5097,7 @@ var Fo = [ try { await No(l.client, O.value, e.user_id), l.notify("success", u("tasks_projects.project.members.detached", { name: t })), await A(); } catch (e) { - l.notify("error", V(e, u("tasks_projects.project.members.detach_failed"))); + l.notify("error", U(e, u("tasks_projects.project.members.detach_failed"))); } finally { C.value = null; } @@ -5184,43 +5201,57 @@ var Fo = [ __name: "ProjectOverviewTab", props: { project: {} }, setup(t) { - let r = t, s = H(), l = n(() => r.project?.totals ?? null), u = n(() => r.project?.budget_minutes ?? null), d = n(() => { - let e = u.value, t = l.value?.logged_minutes ?? 0; + let l = t, u = de(), d = n(() => l.project?.totals ?? null), m = n(() => l.project?.budget_minutes ?? null), h = n(() => { + let e = m.value, t = d.value?.logged_minutes ?? 0; return e ? Math.min(100, Math.round(t / e * 100)) : 0; - }), m = n(() => { - let e = u.value, t = l.value?.logged_minutes ?? 0; + }), _ = n(() => { + let e = l.project?.customer_id ?? null; + return e === null || (d.value?.unbilled_amount ?? 0) <= 0 ? null : `/admin/modules/tasks-projects/billing?customer_id=${e}`; + }), v = n(() => { + let e = m.value, t = d.value?.logged_minutes ?? 0; return e && t > e ? t - e : 0; }); - return (n, r) => { - let h = b("BaseFormatMoney"), _ = b("BaseSpinner"); - return t.project && l.value ? (g(), a("div", Is, [ + return (n, l) => { + let y = b("BaseFormatMoney"), C = b("router-link"), w = b("BaseSpinner"); + return t.project && d.value ? (g(), a("div", Is, [ o("div", Ls, [ o("div", Rs, [ - o("p", zs, x(S(s)("tasks_projects.project.overview.tasks")), 1), - o("p", Bs, x(l.value.tasks.total), 1), - o("p", Vs, x(S(s)("tasks_projects.project.overview.open_tasks", { count: l.value.tasks.open })) + " · " + x(S(s)("tasks_projects.project.overview.closed_tasks", { count: l.value.tasks.closed })), 1) + o("p", zs, x(S(u)("tasks_projects.project.overview.tasks")), 1), + o("p", Bs, x(d.value.tasks.total), 1), + o("p", Vs, x(S(u)("tasks_projects.project.overview.open_tasks", { count: d.value.tasks.open })) + " · " + x(S(u)("tasks_projects.project.overview.closed_tasks", { count: d.value.tasks.closed })), 1) ]), o("div", Hs, [ - o("p", Us, x(S(s)("tasks_projects.project.overview.logged")), 1), - o("p", Ws, x(S(ue)(l.value.logged_minutes)), 1), - o("p", Gs, x(S(s)("tasks_projects.project.overview.billable")) + ": " + x(S(ue)(l.value.billable_minutes)), 1) + o("p", Us, x(S(u)("tasks_projects.project.overview.logged")), 1), + o("p", Ws, x(S(ce)(d.value.logged_minutes)), 1), + o("p", Gs, x(S(u)("tasks_projects.project.overview.billable")) + ": " + x(S(ce)(d.value.billable_minutes)), 1) ]), - o("div", Ks, [o("p", qs, x(S(s)("tasks_projects.project.overview.billable_amount")), 1), o("p", Js, [c(h, { amount: l.value.billable_amount }, null, 8, ["amount"])])]), - o("div", Ys, [o("p", Xs, x(S(s)("tasks_projects.project.overview.unbilled_amount")), 1), o("p", Zs, [c(h, { amount: l.value.unbilled_amount }, null, 8, ["amount"])])]) + o("div", Ks, [o("p", qs, x(S(u)("tasks_projects.project.overview.billable_amount")), 1), o("p", Js, [c(y, { amount: d.value.billable_amount }, null, 8, ["amount"])])]), + o("div", Ys, [ + o("p", Xs, x(S(u)("tasks_projects.project.overview.unbilled_amount")), 1), + o("p", Zs, [c(y, { amount: d.value.unbilled_amount }, null, 8, ["amount"])]), + _.value ? (g(), r(C, { + key: 0, + class: "mt-1 block text-xs font-medium text-primary-500 hover:underline", + to: _.value + }, { + default: E(() => [s(x(S(u)("tasks_projects.billing.view_unbilled")), 1)]), + _: 1 + }, 8, ["to"])) : i("", !0) + ]) ]), - o("div", Qs, [o("p", $s, x(S(s)("tasks_projects.project.overview.budget")), 1), u.value ? (g(), a(e, { key: 0 }, [ - o("p", ec, x(S(s)("tasks_projects.project.overview.budget_used", { - used: S(ue)(l.value.logged_minutes), - total: S(ue)(u.value) + o("div", Qs, [o("p", $s, x(S(u)("tasks_projects.project.overview.budget")), 1), m.value ? (g(), a(e, { key: 0 }, [ + o("p", ec, x(S(u)("tasks_projects.project.overview.budget_used", { + used: S(ce)(d.value.logged_minutes), + total: S(ce)(m.value) })), 1), o("div", tc, [o("div", { - class: f(["h-2 rounded-full", m.value > 0 ? "bg-status-red" : "bg-primary-500"]), - style: p({ width: `${d.value}%` }) + class: f(["h-2 rounded-full", v.value > 0 ? "bg-status-red" : "bg-primary-500"]), + style: p({ width: `${h.value}%` }) }, null, 6)]), - m.value > 0 ? (g(), a("p", nc, x(S(s)("tasks_projects.project.overview.budget_over", { amount: S(ue)(m.value) })), 1)) : i("", !0) - ], 64)) : (g(), a("p", rc, x(S(s)("tasks_projects.project.overview.no_budget")), 1))]), - o("div", ic, [o("p", ac, x(S(s)("tasks_projects.project.overview.description")), 1), t.project.description ? (g(), a("p", oc, x(t.project.description), 1)) : (g(), a("p", sc, x(S(s)("tasks_projects.project.overview.no_description")), 1))]) - ])) : (g(), a("div", cc, [c(_, { class: "h-8 w-8 text-primary-500" })])); + v.value > 0 ? (g(), a("p", nc, x(S(u)("tasks_projects.project.overview.budget_over", { amount: S(ce)(v.value) })), 1)) : i("", !0) + ], 64)) : (g(), a("p", rc, x(S(u)("tasks_projects.project.overview.no_budget")), 1))]), + o("div", ic, [o("p", ac, x(S(u)("tasks_projects.project.overview.description")), 1), t.project.description ? (g(), a("p", oc, x(t.project.description), 1)) : (g(), a("p", sc, x(S(u)("tasks_projects.project.overview.no_description")), 1))]) + ])) : (g(), a("div", cc, [c(w, { class: "h-8 w-8 text-primary-500" })])); }; } }), uc = { class: "relative table-container" }, dc = ["onClick"], fc = { class: "inline-flex items-center" }, pc = { @@ -5244,17 +5275,17 @@ var Fo = [ name: "name", priority: "priority", due_date: "due_date" - }, C = H(), O = v(null), k = v(!0), A = v(0), j = v([]), M = v([]), N = v([]), I = v(!1), L = v(null), ee = v({}), R = v(null), z = _({ + }, C = de(), O = v(null), k = v(!0), A = v(0), j = v([]), M = v([]), N = v([]), I = v(!1), L = v(null), R = v({}), z = v(null), B = _({ search: "", status: null, assignee: null - }), B = n(() => j.value.map((e) => ({ + }), H = n(() => j.value.map((e) => ({ id: e.id, label: e.name - }))), ne = n(() => M.value.map((e) => ({ + }))), ee = n(() => M.value.map((e) => ({ id: e.id, label: e.name - }))), re = n(() => z.search.trim() !== "" || z.status !== null || z.assignee !== null), ie = n(() => !k.value && A.value === 0 && !re.value), ae = n(() => [ + }))), te = n(() => B.search.trim() !== "" || B.status !== null || B.assignee !== null), ne = n(() => !k.value && A.value === 0 && !te.value), re = n(() => [ { key: "number", label: C("tasks_projects.tasks.columns.number"), @@ -5298,31 +5329,31 @@ var Fo = [ sortable: !1, tdClass: "text-right text-sm font-medium" } - ]), oe = { + ]), ie = { LOW: "bg-surface-tertiary text-muted", NORMAL: "bg-primary-50 text-primary-500", HIGH: "bg-alert-warning-bg text-alert-warning-text", URGENT: "bg-alert-error-bg text-alert-error-text" - }, se; - T(() => z.search, () => { - clearTimeout(se), se = setTimeout(() => de(), gc); + }, ae; + T(() => B.search, () => { + clearTimeout(ae), ae = setTimeout(() => le(), gc); }), T([ - () => z.status, - () => z.assignee, + () => B.status, + () => B.assignee, () => u.projectId - ], () => de()), h(() => { - le(); - }), m(() => clearTimeout(se)); - async function le() { + ], () => le()), h(() => { + se(); + }), m(() => clearTimeout(ae)); + async function se() { try { j.value = await wo(u.client); } catch (e) { - u.notify("error", V(e, C("tasks_projects.task_statuses.load_failed"))); + u.notify("error", U(e, C("tasks_projects.task_statuses.load_failed"))); } try { - M.value = await te(u.client); + M.value = await V(u.client); } catch (e) { - u.notify("error", V(e, C("tasks_projects.tasks.members_failed"))); + u.notify("error", U(e, C("tasks_projects.tasks.members_failed"))); } if (!u.projectId) try { let e = await F(u.client, { @@ -5335,16 +5366,16 @@ var Fo = [ label: e.name })); } catch (e) { - u.notify("error", V(e, C("tasks_projects.tasks.projects_failed"))); + u.notify("error", U(e, C("tasks_projects.tasks.projects_failed"))); } } - async function ue({ page: e, sort: t }) { + async function ce({ page: e, sort: t }) { let n = { page: e, limit: hc, ...P(t, y) }; - u.projectId && (n.project_id = u.projectId), z.status && (n.task_status_id = z.status.id), z.assignee && (n.assignee_id = z.assignee.id), z.search.trim() !== "" && (n.search = z.search.trim()), k.value = !0; + u.projectId && (n.project_id = u.projectId), B.status && (n.task_status_id = B.status.id), B.assignee && (n.assignee_id = B.assignee.id), B.search.trim() !== "" && (n.search = B.search.trim()), k.value = !0; try { let e = await To(u.client, n); return A.value = e.meta.total, { @@ -5357,7 +5388,7 @@ var Fo = [ } }; } catch (e) { - return u.notify("error", V(e, C("tasks_projects.tasks.load_failed"))), { + return u.notify("error", U(e, C("tasks_projects.tasks.load_failed"))), { data: [], pagination: { totalPages: 1, @@ -5370,52 +5401,52 @@ var Fo = [ k.value = !1; } } - function de(e = !1) { + function le(e = !1) { O.value?.refresh(e); } + function fe() { + B.search = "", B.status = null, B.assignee = null; + } function pe() { - z.search = "", z.status = null, z.assignee = null; + L.value = null, R.value = { project_id: u.projectId ?? null }, I.value = !0; } - function me() { - L.value = null, ee.value = { project_id: u.projectId ?? null }, I.value = !0; + function me(e) { + L.value = e, R.value = {}, I.value = !0; } function he(e) { - L.value = e, ee.value = {}, I.value = !0; + let t = L.value ? C("tasks_projects.tasks.updated", { name: e.name }) : C("tasks_projects.tasks.created", { name: e.name }); + I.value = !1, L.value = null, u.notify("success", t), le(!0), d("changed"); } function ge(e) { - let t = L.value ? C("tasks_projects.tasks.updated", { name: e.name }) : C("tasks_projects.tasks.created", { name: e.name }); - I.value = !1, L.value = null, u.notify("success", t), de(!0), d("changed"); + I.value = !1, L.value = null, u.notify("success", C("tasks_projects.tasks.deleted", { name: e.name })), le(!0), d("changed"); } function _e(e) { - I.value = !1, L.value = null, u.notify("success", C("tasks_projects.tasks.deleted", { name: e.name })), de(!0), d("changed"); - } - function ve(e) { return j.value.find((t) => t.id === e.task_status_id) ?? null; } - function ye(e) { + function ve(e) { return e.assignee_id === null ? C("tasks_projects.tasks.unassigned") : M.value.find((t) => t.id === e.assignee_id)?.name ?? `#${e.assignee_id}`; } - function be(e) { + function ye(e) { return C(`tasks_projects.tasks.priority.${e.toLowerCase()}`); } - function xe(e) { - return oe[e]; + function be(e) { + return ie[e]; } - async function Se(e) { + async function xe(e) { if (window.confirm(C("tasks_projects.tasks.delete_confirm", { name: e.name }))) { - R.value = e.id; + z.value = e.id; try { - await Oo(u.client, e.id), u.notify("success", C("tasks_projects.tasks.deleted", { name: e.name })), de(!0), d("changed"); + await Oo(u.client, e.id), u.notify("success", C("tasks_projects.tasks.deleted", { name: e.name })), le(!0), d("changed"); } catch (e) { - u.notify("error", V(e, C("tasks_projects.tasks.delete_failed"))); + u.notify("error", U(e, C("tasks_projects.tasks.delete_failed"))); } finally { - R.value = null; + z.value = null; } } } return t({ - openCreate: me, - refresh: de + openCreate: pe, + refresh: le }), (t, n) => { let l = b("BaseInput"), u = b("BaseInputGroup"), d = b("BaseSelectInput"), m = b("BaseFilterWrapper"), h = b("BaseIcon"), _ = b("BaseButton"), v = b("BaseEmptyPlaceholder"), y = b("BaseDropdownItem"), T = b("BaseDropdown"), k = b("BaseTable"); return g(), a("div", null, [ @@ -5423,7 +5454,7 @@ var Fo = [ key: 0, show: !0, class: "mt-3", - onClear: pe + onClear: fe }, { default: E(() => [ c(u, { @@ -5431,8 +5462,8 @@ var Fo = [ class: "mt-2 flex-1" }, { default: E(() => [c(l, { - modelValue: z.search, - "onUpdate:modelValue": n[0] ||= (e) => z.search = e, + modelValue: B.search, + "onUpdate:modelValue": n[0] ||= (e) => B.search = e, type: "text", name: "search", autocomplete: "off", @@ -5445,9 +5476,9 @@ var Fo = [ class: "mt-2 flex-1" }, { default: E(() => [c(d, { - modelValue: z.status, - "onUpdate:modelValue": n[1] ||= (e) => z.status = e, - options: B.value, + modelValue: B.status, + "onUpdate:modelValue": n[1] ||= (e) => B.status = e, + options: H.value, placeholder: S(C)("tasks_projects.tasks.all_tasks"), "label-key": "label" }, null, 8, [ @@ -5462,9 +5493,9 @@ var Fo = [ class: "mt-2 flex-1" }, { default: E(() => [c(d, { - modelValue: z.assignee, - "onUpdate:modelValue": n[2] ||= (e) => z.assignee = e, - options: ne.value, + modelValue: B.assignee, + "onUpdate:modelValue": n[2] ||= (e) => B.assignee = e, + options: ee.value, placeholder: S(C)("tasks_projects.board.filters.all_assignees"), "label-key": "label" }, null, 8, [ @@ -5483,7 +5514,7 @@ var Fo = [ }, { actions: E(() => [c(_, { variant: "primary", - onClick: me + onClick: pe }, { left: E((e) => [c(h, { name: "PlusIcon", @@ -5497,45 +5528,45 @@ var Fo = [ class: "mt-5 mb-4 h-16 w-16 text-subtle" })]), _: 1 - }, 8, ["title", "description"]), [[w, ie.value]]), + }, 8, ["title", "description"]), [[w, ne.value]]), D(o("div", uc, [c(k, { ref_key: "tableRef", ref: O, - data: ue, - columns: ae.value, + data: ce, + columns: re.value, class: "mt-3" }, { "cell-number": E(({ row: e }) => [s("#" + x(e.data.number), 1)]), "cell-name": E(({ row: e }) => [o("button", { type: "button", class: "text-left hover:text-primary-500", - onClick: (t) => he(e.data) + onClick: (t) => me(e.data) }, x(e.data.name), 9, dc)]), "cell-status": E(({ row: e }) => [o("span", fc, [o("span", { - class: f(["mr-2 inline-block h-2.5 w-2.5 shrink-0 rounded-full", ve(e.data)?.colour ? "" : "bg-line-default"]), - style: p(ve(e.data)?.colour ? { backgroundColor: ve(e.data)?.colour } : void 0) - }, null, 6), s(" " + x(ve(e.data)?.name ?? "-"), 1)])]), - "cell-assignee": E(({ row: e }) => [o("span", { class: f(e.data.assignee_id === null ? "text-subtle" : "") }, x(ye(e.data)), 3)]), + class: f(["mr-2 inline-block h-2.5 w-2.5 shrink-0 rounded-full", _e(e.data)?.colour ? "" : "bg-line-default"]), + style: p(_e(e.data)?.colour ? { backgroundColor: _e(e.data)?.colour } : void 0) + }, null, 6), s(" " + x(_e(e.data)?.name ?? "-"), 1)])]), + "cell-assignee": E(({ row: e }) => [o("span", { class: f(e.data.assignee_id === null ? "text-subtle" : "") }, x(ve(e.data)), 3)]), "cell-priority": E(({ row: e }) => [e.data.priority ? (g(), a("span", { key: 0, - class: f(["rounded-full px-2 py-0.5 text-xs font-medium", xe(e.data.priority)]) - }, x(be(e.data.priority)), 3)) : (g(), a("span", pc, "-"))]), + class: f(["rounded-full px-2 py-0.5 text-xs font-medium", be(e.data.priority)]) + }, x(ye(e.data.priority)), 3)) : (g(), a("span", pc, "-"))]), "cell-due_date": E(({ row: e }) => [e.data.due_date ? (g(), a("span", { key: 0, - class: f(S(fe)(e.data.due_date) && !e.data.closed_at ? "font-medium text-status-red" : "") - }, x(S(ce)(e.data.due_date)), 3)) : (g(), a("span", mc, "-"))]), - "cell-actions": E(({ row: e }) => [c(T, { "content-loading": R.value === e.data.id }, { + class: f(S(ue)(e.data.due_date) && !e.data.closed_at ? "font-medium text-status-red" : "") + }, x(S(oe)(e.data.due_date)), 3)) : (g(), a("span", mc, "-"))]), + "cell-actions": E(({ row: e }) => [c(T, { "content-loading": z.value === e.data.id }, { activator: E(() => [c(h, { name: "EllipsisHorizontalIcon", class: "h-5 text-muted" })]), - default: E(() => [c(y, { onClick: (t) => he(e.data) }, { + default: E(() => [c(y, { onClick: (t) => me(e.data) }, { default: E(() => [c(h, { name: "PencilIcon", class: "mr-3 h-5 w-5 text-subtle group-hover:text-muted" }), s(" " + x(S(C)("tasks_projects.general.edit")), 1)]), _: 1 - }, 8, ["onClick"]), c(y, { onClick: (t) => Se(e.data) }, { + }, 8, ["onClick"]), c(y, { onClick: (t) => xe(e.data) }, { default: E(() => [c(h, { name: "TrashIcon", class: "mr-3 h-5 w-5 text-subtle group-hover:text-muted" @@ -5545,7 +5576,7 @@ var Fo = [ _: 2 }, 1032, ["content-loading"])]), _: 1 - }, 8, ["columns"])], 512), [[w, !ie.value]]), + }, 8, ["columns"])], 512), [[w, !ne.value]]), c(Ho, { show: I.value, client: e.client, @@ -5554,11 +5585,11 @@ var Fo = [ statuses: j.value, members: M.value, projects: N.value, - defaults: ee.value, + defaults: R.value, "lock-project": !!e.projectId, onClose: n[3] ||= (e) => I.value = !1, - onSaved: ge, - onDeleted: _e + onSaved: he, + onDeleted: ge }, null, 8, [ "show", "client", @@ -5583,7 +5614,7 @@ var Fo = [ }, emits: ["refresh"], setup(e, { emit: t }) { - let r = e, i = t, l = H(), u = v(null), d = n(() => r.project?.id ?? Number(r.id)); + let r = e, i = t, l = de(), u = v(null), d = n(() => r.project?.id ?? Number(r.id)); function p() { i("refresh"); } @@ -5632,7 +5663,7 @@ var Fo = [ project: {} }, setup(e) { - let t = e, l = H(), u = v([]), d = v([]), f = n(() => t.project?.id ?? Number(t.id)), p = n(() => [ + let t = e, l = de(), u = v([]), d = v([]), f = n(() => t.project?.id ?? Number(t.id)), p = n(() => [ { key: "started_at", label: l("tasks_projects.project.time.columns.date"), @@ -5671,9 +5702,9 @@ var Fo = [ }); async function m() { try { - u.value = await te(t.client); + u.value = await V(t.client); } catch (e) { - t.notify("error", V(e, l("tasks_projects.tasks.members_failed"))); + t.notify("error", U(e, l("tasks_projects.tasks.members_failed"))); } try { let e = await To(t.client, { @@ -5682,7 +5713,7 @@ var Fo = [ }); d.value = e.data; } catch (e) { - t.notify("error", V(e, l("tasks_projects.tasks.load_failed"))); + t.notify("error", U(e, l("tasks_projects.tasks.load_failed"))); } } async function _({ page: e }) { @@ -5703,7 +5734,7 @@ var Fo = [ } }; } catch (e) { - return t.notify("error", V(e, l("tasks_projects.project.time.load_failed"))), { + return t.notify("error", U(e, l("tasks_projects.project.time.load_failed"))), { data: [], pagination: { totalPages: 1, @@ -5726,10 +5757,10 @@ var Fo = [ data: _, columns: p.value }, { - "cell-started_at": E(({ row: e }) => [s(x(e.data.started_at ? S(ce)(e.data.started_at) : "-"), 1)]), + "cell-started_at": E(({ row: e }) => [s(x(e.data.started_at ? S(oe)(e.data.started_at) : "-"), 1)]), "cell-user": E(({ row: e }) => [s(x(y(e.data.user_id)), 1)]), "cell-task": E(({ row: e }) => [o("span", Sc, x(C(e.data.task_id)), 1), e.data.description ? (g(), a("span", Cc, x(e.data.description), 1)) : i("", !0)]), - "cell-duration_minutes": E(({ row: e }) => [e.data.is_running ? (g(), a("span", wc, x(S(l)("tasks_projects.project.time.running")), 1)) : (g(), a("span", Tc, x(S(ue)(e.data.duration_minutes)), 1))]), + "cell-duration_minutes": E(({ row: e }) => [e.data.is_running ? (g(), a("span", wc, x(S(l)("tasks_projects.project.time.running")), 1)) : (g(), a("span", Tc, x(S(ce)(e.data.duration_minutes)), 1))]), "cell-billable": E(({ row: e }) => [e.data.billable ? (g(), r(n, { key: 0, name: "CheckCircleIcon", @@ -5747,7 +5778,7 @@ var Fo = [ notify: { type: Function } }, setup(e) { - let t = H(), n = v(null); + let t = de(), n = v(null); return (i, a) => { let l = b("BaseBreadcrumbItem"), u = b("BaseBreadcrumb"), d = b("BaseIcon"), p = b("BaseButton"), m = b("router-link"), h = b("BasePageHeader"), _ = b("BasePage"); return g(), r(_, null, { @@ -5825,7 +5856,7 @@ var Mc = "tasks-projects", Nc = { viewOwnTime: `${Mc}:view-own-time` }; function Pc(e) { - e.addMessages(si), e.registerPage({ + e.addMessages(oi), e.registerPage({ id: "board", module: Mc, path: "board", @@ -5893,8 +5924,845 @@ function Pc(e) { }); } //#endregion +//#region resources/js/messages/billing.ts +var Fc = { en: { tasks_projects: { billing: { + title: "Invoice time", + subtitle: "Turn unbilled hours into a draft invoice.", + invoice_time: "Invoice time", + unbilled: "Unbilled", + view_unbilled: "Invoice this time", + steps: { + customer: "Customer", + entries: "Entries", + preview: "Preview", + create: "Create" + }, + back: "Back", + next: "Continue", + start_over: "Start over", + customer: { + title: "Who are you invoicing?", + description: "Customers with billable time that has not reached an invoice yet.", + entries: "{count} entries", + empty_title: "Nothing to invoice", + empty_description: "Billable time appears here once it has been logged against a task that belongs to a customer.", + load_failed: "Unable to load the customers with unbilled time.", + names_failed: "Unable to load the customer names; ids are shown instead.", + unnamed: "Customer #{id}", + from: "From", + to: "To", + clear_range: "Clear dates" + }, + entries: { + title: "Which time goes on the invoice?", + grouping: "Group lines by", + group_by: { + task: "Task", + project: "Project", + member: "Member", + summary: "One summary line" + }, + select_all: "Select all", + selected: "{count} of {total} entries selected", + selected_total: "Selected: {hours}", + no_description: "No description", + columns: { + date: "Date", + task: "Task", + project: "Project", + member: "Member", + duration: "Duration", + amount: "Amount" + }, + empty_title: "No unbilled time", + empty_description: "This customer has nothing waiting to be invoiced in this range.", + load_failed: "Unable to load the unbilled time.", + none_selected: "Select at least one entry." + }, + preview: { + title: "Check the invoice", + lines: "Invoice lines", + columns: { + description: "Description", + quantity: "Hours", + price: "Rate", + total: "Amount" + }, + sub_total: "Subtotal", + total: "Total", + invoice_date: "Invoice date", + due_date: "Due date", + invoice_number: "Invoice number", + invoice_number_auto: "Generated by the company number format.", + template: "Template", + exchange_rate: "Exchange rate", + exchange_rate_help: "1 {currency} in the company currency.", + prepare_failed: "Unable to prepare the invoice.", + templates_failed: "Unable to load the invoice templates.", + number_failed: "Unable to read the next invoice number. Type one in.", + rate_failed: "Unable to read the exchange rate. Type one in.", + create: "Create invoice", + invalid: "The invoice was refused. Fix the fields below and try again." + }, + create: { + creating: "Creating the invoice", + stamping: "Marking the time as invoiced", + created_title: "Invoice {number} created", + created_description: "{count} entries were marked as invoiced.", + view_invoice: "Open the invoice", + invoice_more: "Invoice more time", + failed: "Unable to create the invoice.", + stamp_failed_title: "The invoice was created, but the time is not marked yet", + stamp_failed_description: "Invoice {number} exists. The time entries still count as unbilled until they are stamped, which is safe to run again.", + retry_stamp: "Retry stamping", + stamped: "The time entries were marked as invoiced." + } +} } } }, Ic = "/api/v1/tasks-projects", Lc = { + customers: `${Ic}/billing/customers`, + unbilled: `${Ic}/billing/unbilled`, + prepare: `${Ic}/billing/prepare`, + confirm: `${Ic}/billing/confirm` +}, Rc = { + bootstrap: "/api/v1/bootstrap", + customers: "/api/v1/customers", + invoices: "/api/v1/invoices", + invoiceTemplates: "/api/v1/invoices/templates", + nextNumber: "/api/v1/next-number", + exchangeRate: (e) => `/api/v1/currencies/${e}/exchange-rate` +}; +async function zc(e, t = {}) { + let { data: n } = await e.get(Lc.customers, { params: t }); + return n.data ?? []; +} +async function Bc(e, t, n = {}) { + let { data: r } = await e.get(Lc.unbilled, { params: { + customer_id: t, + ...n + } }); + return r.data; +} +async function Vc(e, t, n) { + let { data: r } = await e.post(Lc.prepare, { + entry_ids: t, + grouping: n + }); + return r.data; +} +async function Hc(e, t, n) { + let { data: r } = await e.post(Lc.confirm, { + invoice_id: t, + items: n + }); + return r?.stamped ?? 0; +} +async function Uc(e, t = 200) { + let { data: n } = await e.get(Rc.customers, { params: { limit: t } }); + return n.data ?? []; +} +async function Wc(e, t) { + let { data: n } = await e.post(Rc.invoices, t); + return n.data; +} +async function Gc(e) { + let { data: t } = await e.get(Rc.invoiceTemplates); + return t?.invoiceTemplates ?? []; +} +async function Kc(e, t) { + let n = { key: "invoice" }; + t !== void 0 && (n.userId = t); + let { data: r } = await e.get(Rc.nextNumber, { params: n }); + return r?.success && typeof r.nextNumber == "string" ? r.nextNumber : null; +} +async function qc(e, t) { + let { data: n } = await e.get(Rc.exchangeRate(t)), r = Array.isArray(n?.exchangeRate) ? n.exchangeRate[0] : n?.exchangeRate, i = Number(r); + return Number.isFinite(i) && i > 0 ? i : null; +} +async function Jc(e) { + let { data: t } = await e.get(Rc.bootstrap), n = t?.current_company_settings ?? {}, r = t?.current_user_settings ?? {}, i = Number(n.invoice_due_date_days), a = r.default_invoice_template; + return { + currency: t?.current_company_currency ?? null, + dueDateDays: Number.isFinite(i) && i >= 0 ? i : 0, + setDueDateAutomatically: n.invoice_set_due_date_automatically === "YES", + autoGenerateNumber: n.invoice_auto_generate !== "NO", + defaultTemplate: typeof a == "string" && a !== "" ? a : null + }; +} +//#endregion +//#region resources/js/pages/BillingPage.vue?vue&type=script&setup=true&lang.ts +var Yc = { class: "flex items-center justify-end space-x-5" }, Xc = { class: "mt-6 flex flex-wrap items-center gap-x-6 gap-y-3" }, Zc = { + key: 0, + class: "flex justify-center py-16" +}, Qc = { + key: 1, + class: "mt-6" +}, $c = { class: "text-base font-semibold text-heading" }, el = { class: "mt-1 text-sm text-muted" }, tl = { class: "mt-4 flex flex-wrap items-end gap-4" }, nl = { + key: 0, + class: "mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3" +}, rl = ["onClick"], il = { class: "text-sm font-semibold text-heading" }, al = { class: "mt-1 text-xs text-muted" }, ol = { class: "mt-3 text-xl font-semibold text-heading" }, sl = { + key: 2, + class: "mt-6" +}, cl = { class: "flex flex-wrap items-end justify-between gap-4" }, ll = { class: "text-base font-semibold text-heading" }, ul = { class: "mt-1 text-sm text-muted" }, dl = { + key: 0, + class: "flex justify-center py-16" +}, fl = { class: "mt-4 flex flex-wrap items-center justify-between gap-3 rounded-lg border border-line-default bg-surface-secondary px-4 py-3" }, pl = { class: "flex cursor-pointer items-center gap-2 text-sm font-medium text-heading" }, ml = ["checked"], hl = { class: "text-sm text-muted" }, gl = { class: "flex flex-wrap items-center justify-between gap-3 bg-surface-secondary px-4 py-3" }, _l = { class: "flex cursor-pointer items-center gap-2 text-sm font-semibold text-heading" }, vl = ["checked", "onChange"], yl = { class: "text-sm text-muted" }, bl = { class: "overflow-x-auto" }, xl = { class: "w-full table-auto" }, Sl = { class: "bg-surface text-xs tracking-wider text-muted uppercase" }, Cl = { class: "px-4 py-2 text-left font-medium" }, wl = { class: "px-4 py-2 text-left font-medium" }, Tl = { class: "px-4 py-2 text-left font-medium" }, El = { class: "px-4 py-2 text-left font-medium" }, Dl = { class: "px-4 py-2 text-right font-medium" }, Ol = { class: "px-4 py-2 text-right font-medium" }, kl = { class: "divide-y divide-line-default bg-surface text-sm" }, Al = { class: "pl-4" }, jl = ["checked", "onChange"], Ml = { class: "px-4 py-2 whitespace-nowrap text-muted" }, Nl = { class: "px-4 py-2" }, Pl = { class: "text-heading" }, Fl = { class: "block text-xs text-subtle" }, Il = { class: "px-4 py-2 text-muted" }, Ll = { class: "px-4 py-2 text-muted" }, Rl = { class: "px-4 py-2 text-right whitespace-nowrap text-muted" }, zl = { class: "px-4 py-2 text-right whitespace-nowrap text-heading" }, Bl = { class: "mt-5 flex flex-wrap items-center justify-between gap-4" }, Vl = { class: "text-sm font-medium text-heading" }, Hl = { class: "flex items-center gap-3" }, Ul = { + key: 3, + class: "mt-6" +}, Wl = { class: "text-base font-semibold text-heading" }, Gl = { + key: 0, + class: "flex justify-center py-16" +}, Kl = { + key: 0, + class: "mt-4 rounded-lg border border-status-red bg-surface px-4 py-3 text-sm text-status-red" +}, ql = { class: "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4" }, Jl = { + key: 0, + class: "mt-1 block text-xs text-subtle" +}, Yl = { class: "mt-1 block text-xs text-subtle" }, Xl = { class: "mt-5 overflow-hidden rounded-xl border border-line-default" }, Zl = { class: "overflow-x-auto" }, Ql = { class: "w-full table-auto" }, $l = { class: "bg-surface-secondary text-xs tracking-wider text-muted uppercase" }, eu = { class: "px-4 py-2 text-left font-medium" }, tu = { class: "px-4 py-2 text-right font-medium" }, nu = { class: "px-4 py-2 text-right font-medium" }, ru = { class: "px-4 py-2 text-right font-medium" }, iu = { class: "divide-y divide-line-default bg-surface text-sm" }, au = { class: "px-4 py-3" }, ou = { class: "font-medium text-heading" }, su = { + key: 0, + class: "mt-1 block text-xs whitespace-pre-line text-subtle" +}, cu = { class: "px-4 py-3 text-right whitespace-nowrap text-muted" }, lu = { class: "px-4 py-3 text-right whitespace-nowrap text-muted" }, uu = { class: "px-4 py-3 text-right whitespace-nowrap font-medium text-heading" }, du = { class: "bg-surface-secondary text-sm" }, fu = { + class: "px-4 py-2 text-right text-muted", + colspan: "3" +}, pu = { class: "px-4 py-2 text-right whitespace-nowrap text-heading" }, mu = { + class: "px-4 py-2 text-right font-semibold text-heading", + colspan: "3" +}, hu = { class: "px-4 py-2 text-right whitespace-nowrap font-semibold text-heading" }, gu = { class: "mt-5 flex items-center justify-end gap-3" }, _u = { + key: 4, + class: "mt-6" +}, vu = { + key: 0, + class: "flex flex-col items-center gap-3 py-16" +}, yu = { class: "text-sm text-muted" }, bu = { + key: 0, + class: "rounded-xl border border-status-yellow bg-surface p-5" +}, xu = { class: "text-sm font-semibold text-heading" }, Su = { class: "mt-1 text-sm text-muted" }, Cu = { class: "mt-4 flex flex-wrap items-center gap-3" }, wu = { + key: 1, + class: "rounded-xl border border-line-default bg-surface p-6 text-center" +}, Tu = { class: "mt-3 text-base font-semibold text-heading" }, Eu = { class: "mt-1 text-sm text-muted" }, Du = { class: "mt-3 text-2xl font-semibold text-heading" }, Ou = { class: "mt-5 flex flex-wrap items-center justify-center gap-3" }, ku = { + key: 2, + class: "flex justify-center py-16" +}, Au = "/admin/invoices", ju = /* @__PURE__ */ l({ + __name: "BillingPage", + props: { + client: { type: [Function, Object] }, + notify: { type: Function }, + router: {} + }, + setup(t) { + let l = t, u = [ + "task", + "project", + "member", + "summary" + ], d = de(), p = v(1), m = v(!0), C = v([]), w = v({}), D = _({ + from: "", + to: "" + }), O = v(null), k = v(null), A = v("task"), j = v([]), M = v(!1), N = v(null), P = v([]), F = v(null), I = v(!1), L = _({ + invoiceDate: "", + dueDate: "", + invoiceNumber: "", + templateName: "", + exchangeRate: "" + }), R = v({}), z = v(!1), B = v(!1), V = v(null), H = v(null), ee = v(!1), ne = v(!1), re = n(() => [ + d("tasks_projects.billing.steps.customer"), + d("tasks_projects.billing.steps.entries"), + d("tasks_projects.billing.steps.preview"), + d("tasks_projects.billing.steps.create") + ]), ie = n(() => u.map((e) => ({ + id: e, + label: d(`tasks_projects.billing.entries.group_by.${e}`) + }))), ae = n({ + get: () => ie.value.find((e) => e.id === A.value) ?? ie.value[0], + set: (e) => { + A.value = e.id; + } + }), le = n(() => P.value.map((e) => ({ name: e.name }))), ue = n({ + get: () => le.value.find((e) => e.name === L.templateName) ?? { name: L.templateName }, + set: (e) => { + L.templateName = e.name; + } + }), fe = n(() => (k.value?.entries ?? []).filter((e) => e.currency_id === (O.value?.currency_id ?? null))), pe = n(() => { + let e = {}; + for (let t of fe.value) e[t.id] = t; + return e; + }), me = n(() => (k.value?.groups[A.value] ?? []).filter((e) => e.currency_id === (O.value?.currency_id ?? null))), he = n(() => fe.value.length), ge = n(() => he.value > 0 && j.value.length === he.value), _e = n(() => j.value.reduce((e, t) => e + (pe.value[t]?.minutes ?? 0), 0)), ve = n(() => j.value.reduce((e, t) => e + (pe.value[t]?.amount ?? 0), 0)), ye = n(() => O.value === null ? null : je(O.value.customer_id, O.value.currency_id)), be = n(() => { + let e = N.value?.currency?.id ?? null; + return e === null || O.value === null ? !1 : Ae(O.value.customer_id) !== e; + }), xe = n(() => N.value?.autoGenerateNumber !== !0 || R.value.invoice_number !== void 0), Se = n(() => { + let e = {}; + return D.from !== "" && (e.from = D.from), D.to !== "" && (e.to = D.to), e; + }); + T(() => [D.from, D.to], () => void Ee()), h(() => void Ce()); + async function Ce() { + m.value = !0, await Promise.all([ + Te(), + Ee(), + De(), + Oe() + ]), m.value = !1; + let e = we(); + if (e !== null) { + let t = C.value.find((t) => t.customer_id === e); + t && await Fe(t); + } + } + function we() { + let e = l.router.currentRoute.value.query.customer_id, t = Array.isArray(e) ? e[0] : e ?? new URLSearchParams(window.location.search).get("customer_id"), n = Number(t); + return Number.isInteger(n) && n > 0 ? n : null; + } + async function Te() { + try { + N.value = await Jc(l.client); + } catch { + N.value = null; + } + } + async function Ee() { + try { + C.value = await zc(l.client, Se.value); + } catch (e) { + C.value = [], l.notify("error", U(e, d("tasks_projects.billing.customer.load_failed"))); + } + } + async function De() { + try { + let e = {}; + for (let t of await Uc(l.client)) e[t.id] = t; + w.value = e; + } catch { + w.value = {}; + } + } + async function Oe() { + try { + P.value = await Gc(l.client); + } catch (e) { + P.value = [], l.notify("error", U(e, d("tasks_projects.billing.preview.templates_failed"))); + } + } + function ke(e) { + let t = w.value[e], n = t?.display_name ?? t?.name; + return n && n !== "" ? n : d("tasks_projects.billing.customer.unnamed", { id: e }); + } + function Ae(e) { + let t = w.value[e]; + return t?.currency_id ?? t?.currency?.id ?? null; + } + function je(e, t) { + if (t === null) return null; + let n = w.value[e]; + if (n?.currency && n.currency.id === t) return n.currency; + let r = N.value?.currency ?? null; + return r !== null && r.id === t ? r : null; + } + function Me() { + D.from = "", D.to = ""; + } + function Ne(e) { + D.from = e ? se(e) : ""; + } + function Pe(e) { + D.to = e ? se(e) : ""; + } + async function Fe(e) { + O.value = e, p.value = 2, M.value = !0, k.value = null, j.value = []; + try { + k.value = await Bc(l.client, e.customer_id, Se.value), j.value = fe.value.map((e) => e.id); + } catch (e) { + l.notify("error", U(e, d("tasks_projects.billing.entries.load_failed"))); + } finally { + M.value = !1; + } + } + function Ie(e) { + return j.value.includes(e); + } + function Le(e) { + j.value = Ie(e) ? j.value.filter((t) => t !== e) : [...j.value, e]; + } + function Re() { + j.value = ge.value ? [] : fe.value.map((e) => e.id); + } + function ze(e) { + return e.entry_ids.length > 0 && e.entry_ids.every((e) => Ie(e)); + } + function Be(e) { + if (ze(e)) { + j.value = j.value.filter((t) => !e.entry_ids.includes(t)); + return; + } + let t = e.entry_ids.filter((e) => !Ie(e)); + j.value = [...j.value, ...t]; + } + function Ve(e) { + return e.entry_ids.map((e) => pe.value[e]).filter((e) => e !== void 0); + } + async function He() { + if (j.value.length === 0) { + l.notify("warning", d("tasks_projects.billing.entries.none_selected")); + return; + } + p.value = 3, I.value = !0, F.value = null, R.value = {}, z.value = !1; + try { + let e = await Vc(l.client, j.value, A.value); + F.value = e, await Ue(e); + } catch (e) { + l.notify("error", U(e, d("tasks_projects.billing.preview.prepare_failed"))), p.value = 2; + } finally { + I.value = !1; + } + } + async function Ue(e) { + L.invoiceDate = e.invoice_date, L.dueDate = We(e.invoice_date), L.templateName = N.value?.defaultTemplate ?? P.value[0]?.name ?? ""; + let [t, n] = await Promise.all([Kc(l.client, e.customer_id).catch(() => null), be.value && O.value?.currency_id ? qc(l.client, O.value.currency_id).catch(() => null) : Promise.resolve(null)]); + L.invoiceNumber = t ?? "", t === null && l.notify("warning", d("tasks_projects.billing.preview.number_failed")), be.value ? (L.exchangeRate = n === null ? "" : String(n), n === null && l.notify("warning", d("tasks_projects.billing.preview.rate_failed"))) : L.exchangeRate = ""; + } + function We(e) { + let t = N.value; + if (t === null || !t.setDueDateAutomatically) return ""; + let n = /* @__PURE__ */ new Date(`${e}T00:00:00`); + return Number.isNaN(n.getTime()) ? "" : (n.setDate(n.getDate() + t.dueDateDays), se(n)); + } + function Ge(e) { + L.invoiceDate = e ? se(e) : "", L.dueDate = We(L.invoiceDate); + } + function Ke(e) { + L.dueDate = e ? se(e) : ""; + } + function qe(e) { + return { + invoice_date: L.invoiceDate, + due_date: L.dueDate === "" ? null : L.dueDate, + customer_id: e.customer_id, + invoice_number: L.invoiceNumber, + currency_id: Ae(e.customer_id) ?? e.currency_id, + exchange_rate: be.value && L.exchangeRate !== "" ? Number(L.exchangeRate) : null, + discount: e.discount, + discount_type: e.discount_type, + discount_val: e.discount_val, + tax: e.tax, + sub_total: e.sub_total, + total: e.total, + tax_included: !1, + notes: e.notes, + template_name: L.templateName, + items: e.items.map((e) => ({ ...e })), + taxes: [] + }; + } + async function Je() { + let e = F.value; + if (!(e === null || B.value)) { + p.value = 4, B.value = !0, R.value = {}, z.value = !1, V.value = null, H.value = null, ee.value = !1; + try { + let t = await Wc(l.client, qe(e)); + V.value = t, await Ye(t, e); + } catch (e) { + gt(e) === 422 ? (R.value = te(e), z.value = !0, p.value = 3, l.notify("error", U(e, d("tasks_projects.billing.preview.invalid")))) : (p.value = 3, l.notify("error", U(e, d("tasks_projects.billing.create.failed")))); + } finally { + B.value = !1; + } + } + } + async function Ye(e, t) { + let n = e.items ?? [], r = []; + if (t.groups.forEach((e, t) => { + let i = n[t]; + i && r.push({ + invoice_item_id: i.id, + entry_ids: e.entry_ids + }); + }), r.length === 0) { + ee.value = !0; + return; + } + ne.value = !0; + try { + H.value = await Hc(l.client, e.id, r), ee.value = !1; + } catch (e) { + ee.value = !0, l.notify("error", U(e, d("tasks_projects.billing.create.stamp_failed_title"))); + } finally { + ne.value = !1; + } + } + async function Xe() { + let e = V.value, t = F.value; + e === null || t === null || ne.value || (await Ye(e, t), ee.value || l.notify("success", d("tasks_projects.billing.create.stamped"))); + } + async function Ze() { + p.value = 1, O.value = null, k.value = null, F.value = null, V.value = null, H.value = null, ee.value = !1, j.value = [], R.value = {}, z.value = !1, await Ee(); + } + function Qe(e) { + return p.value > e ? "border-primary-500 bg-primary-500 text-white" : p.value === e ? "border-primary-500 text-primary-500" : "border-line-default text-subtle"; + } + return (t, n) => { + let l = b("BaseBreadcrumbItem"), u = b("BaseBreadcrumb"), h = b("BaseIcon"), _ = b("BaseButton"), v = b("router-link"), w = b("BasePageHeader"), T = b("BaseSpinner"), k = b("BaseDatePicker"), A = b("BaseInputGroup"), N = b("BaseFormatMoney"), P = b("BaseEmptyPlaceholder"), U = b("BaseSelectInput"), te = b("BaseInput"), se = b("BasePage"); + return g(), r(se, null, { + default: E(() => [ + c(w, { title: S(d)("tasks_projects.billing.title") }, { + actions: E(() => [o("div", Yc, [c(v, { to: "/admin/modules/tasks-projects/time" }, { + default: E(() => [c(_, { variant: "white" }, { + left: E((e) => [c(h, { + name: "ClockIcon", + class: f(e.class) + }, null, 8, ["class"])]), + default: E(() => [s(" " + x(S(d)("tasks_projects.time.title")), 1)]), + _: 1 + })]), + _: 1 + })])]), + default: E(() => [c(u, null, { + default: E(() => [ + c(l, { + title: S(d)("tasks_projects.general.home"), + to: "/admin/dashboard" + }, null, 8, ["title"]), + c(l, { + title: S(d)("tasks_projects.projects.title"), + to: "/admin/modules/tasks-projects" + }, null, 8, ["title"]), + c(l, { + title: S(d)("tasks_projects.billing.title"), + to: "#", + active: "" + }, null, 8, ["title"]) + ]), + _: 1 + })]), + _: 1 + }, 8, ["title"]), + o("ol", Xc, [(g(!0), a(e, null, y(re.value, (e, t) => (g(), a("li", { + key: e, + class: "flex items-center gap-2" + }, [o("span", { class: f(["flex h-7 w-7 items-center justify-center rounded-full border text-xs font-semibold", Qe(t + 1)]) }, x(t + 1), 3), o("span", { class: f(["text-sm font-medium", p.value === t + 1 ? "text-heading" : "text-muted"]) }, x(e), 3)]))), 128))]), + m.value ? (g(), a("div", Zc, [c(T, { class: "h-8 w-8 text-primary-500" })])) : p.value === 1 ? (g(), a("section", Qc, [ + o("h2", $c, x(S(d)("tasks_projects.billing.customer.title")), 1), + o("p", el, x(S(d)("tasks_projects.billing.customer.description")), 1), + o("div", tl, [ + c(A, { + label: S(d)("tasks_projects.billing.customer.from"), + class: "w-full sm:w-48" + }, { + default: E(() => [c(k, { + "model-value": D.from, + "onUpdate:modelValue": Ne + }, null, 8, ["model-value"])]), + _: 1 + }, 8, ["label"]), + c(A, { + label: S(d)("tasks_projects.billing.customer.to"), + class: "w-full sm:w-48" + }, { + default: E(() => [c(k, { + "model-value": D.to, + "onUpdate:modelValue": Pe + }, null, 8, ["model-value"])]), + _: 1 + }, 8, ["label"]), + D.from !== "" || D.to !== "" ? (g(), r(_, { + key: 0, + variant: "primary-outline", + onClick: Me + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.customer.clear_range")), 1)]), + _: 1 + })) : i("", !0) + ]), + C.value.length > 0 ? (g(), a("div", nl, [(g(!0), a(e, null, y(C.value, (e) => (g(), a("button", { + key: `${e.customer_id}-${e.currency_id ?? "none"}`, + type: "button", + class: "rounded-xl border border-line-default bg-surface p-5 text-left transition hover:border-primary-500", + onClick: (t) => Fe(e) + }, [ + o("p", il, x(ke(e.customer_id)), 1), + o("p", al, x(S(d)("tasks_projects.billing.customer.entries", { count: e.entries })) + " · " + x(S(ce)(e.minutes)), 1), + o("p", ol, [c(N, { + amount: e.amount, + currency: je(e.customer_id, e.currency_id) + }, null, 8, ["amount", "currency"])]) + ], 8, rl))), 128))])) : (g(), r(P, { + key: 1, + title: S(d)("tasks_projects.billing.customer.empty_title"), + description: S(d)("tasks_projects.billing.customer.empty_description") + }, { + default: E(() => [c(h, { + name: "BanknotesIcon", + class: "mt-5 mb-4 h-16 w-16 text-subtle" + })]), + _: 1 + }, 8, ["title", "description"])) + ])) : p.value === 2 ? (g(), a("section", sl, [o("div", cl, [o("div", null, [o("h2", ll, x(S(d)("tasks_projects.billing.entries.title")), 1), o("p", ul, x(O.value ? ke(O.value.customer_id) : ""), 1)]), c(A, { + label: S(d)("tasks_projects.billing.entries.grouping"), + class: "w-full sm:w-56" + }, { + default: E(() => [c(U, { + modelValue: ae.value, + "onUpdate:modelValue": n[0] ||= (e) => ae.value = e, + options: ie.value, + "label-key": "label" + }, null, 8, ["modelValue", "options"])]), + _: 1 + }, 8, ["label"])]), M.value ? (g(), a("div", dl, [c(T, { class: "h-8 w-8 text-primary-500" })])) : he.value > 0 ? (g(), a(e, { key: 1 }, [ + o("div", fl, [o("label", pl, [o("input", { + type: "checkbox", + class: "h-4 w-4 cursor-pointer rounded border-line-strong", + checked: ge.value, + onChange: Re + }, null, 40, ml), s(" " + x(S(d)("tasks_projects.billing.entries.select_all")), 1)]), o("p", hl, x(S(d)("tasks_projects.billing.entries.selected", { + count: j.value.length, + total: he.value + })), 1)]), + (g(!0), a(e, null, y(me.value, (t) => (g(), a("div", { + key: `${t.label}-${t.key ?? "none"}-${t.currency_id ?? "none"}`, + class: "mt-4 overflow-hidden rounded-xl border border-line-default" + }, [o("div", gl, [o("label", _l, [o("input", { + type: "checkbox", + class: "h-4 w-4 cursor-pointer rounded border-line-strong", + checked: ze(t), + onChange: (e) => Be(t) + }, null, 40, vl), s(" " + x(t.label), 1)]), o("p", yl, [s(x(S(ce)(t.minutes)) + " · ", 1), c(N, { + amount: t.amount, + currency: ye.value + }, null, 8, ["amount", "currency"])])]), o("div", bl, [o("table", xl, [o("thead", Sl, [o("tr", null, [ + n[6] ||= o("th", { class: "w-10" }, null, -1), + o("th", Cl, x(S(d)("tasks_projects.billing.entries.columns.date")), 1), + o("th", wl, x(S(d)("tasks_projects.billing.entries.columns.task")), 1), + o("th", Tl, x(S(d)("tasks_projects.billing.entries.columns.project")), 1), + o("th", El, x(S(d)("tasks_projects.billing.entries.columns.member")), 1), + o("th", Dl, x(S(d)("tasks_projects.billing.entries.columns.duration")), 1), + o("th", Ol, x(S(d)("tasks_projects.billing.entries.columns.amount")), 1) + ])]), o("tbody", kl, [(g(!0), a(e, null, y(Ve(t), (e) => (g(), a("tr", { key: e.id }, [ + o("td", Al, [o("input", { + type: "checkbox", + class: "h-4 w-4 cursor-pointer rounded border-line-strong", + checked: Ie(e.id), + onChange: (t) => Le(e.id) + }, null, 40, jl)]), + o("td", Ml, x(S(oe)(e.date)), 1), + o("td", Nl, [o("span", Pl, x(e.task_name), 1), o("span", Fl, x(e.description || S(d)("tasks_projects.billing.entries.no_description")), 1)]), + o("td", Il, x(e.project_name ?? "-"), 1), + o("td", Ll, x(e.user_name), 1), + o("td", Rl, x(S(ce)(e.minutes)), 1), + o("td", zl, [c(N, { + amount: e.amount, + currency: ye.value + }, null, 8, ["amount", "currency"])]) + ]))), 128))])])])]))), 128)), + o("div", Bl, [o("p", Vl, [s(x(S(d)("tasks_projects.billing.entries.selected_total", { hours: S(ce)(_e.value) })) + " · ", 1), c(N, { + amount: ve.value, + currency: ye.value + }, null, 8, ["amount", "currency"])]), o("div", Hl, [c(_, { + variant: "primary-outline", + onClick: Ze + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.back")), 1)]), + _: 1 + }), c(_, { + variant: "primary", + disabled: j.value.length === 0, + onClick: He + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.next")), 1)]), + _: 1 + }, 8, ["disabled"])])]) + ], 64)) : (g(), r(P, { + key: 2, + title: S(d)("tasks_projects.billing.entries.empty_title"), + description: S(d)("tasks_projects.billing.entries.empty_description") + }, { + actions: E(() => [c(_, { + variant: "primary", + onClick: Ze + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.back")), 1)]), + _: 1 + })]), + default: E(() => [c(h, { + name: "ClockIcon", + class: "mt-5 mb-4 h-16 w-16 text-subtle" + })]), + _: 1 + }, 8, ["title", "description"]))])) : p.value === 3 ? (g(), a("section", Ul, [o("h2", Wl, x(S(d)("tasks_projects.billing.preview.title")), 1), I.value || F.value === null ? (g(), a("div", Gl, [c(T, { class: "h-8 w-8 text-primary-500" })])) : (g(), a(e, { key: 1 }, [ + z.value ? (g(), a("div", Kl, x(S(d)("tasks_projects.billing.preview.invalid")), 1)) : i("", !0), + o("div", ql, [ + c(A, { + label: S(d)("tasks_projects.billing.preview.invoice_date"), + error: R.value.invoice_date, + required: "" + }, { + default: E(() => [c(k, { + "model-value": L.invoiceDate, + "onUpdate:modelValue": Ge + }, null, 8, ["model-value"])]), + _: 1 + }, 8, ["label", "error"]), + c(A, { + label: S(d)("tasks_projects.billing.preview.due_date"), + error: R.value.due_date + }, { + default: E(() => [c(k, { + "model-value": L.dueDate, + "onUpdate:modelValue": Ke + }, null, 8, ["model-value"])]), + _: 1 + }, 8, ["label", "error"]), + c(A, { + label: S(d)("tasks_projects.billing.preview.invoice_number"), + error: R.value.invoice_number, + required: "" + }, { + default: E(() => [c(te, { + modelValue: L.invoiceNumber, + "onUpdate:modelValue": n[1] ||= (e) => L.invoiceNumber = e, + type: "text", + name: "invoice_number", + disabled: !xe.value + }, null, 8, ["modelValue", "disabled"]), xe.value ? i("", !0) : (g(), a("span", Jl, x(S(d)("tasks_projects.billing.preview.invoice_number_auto")), 1))]), + _: 1 + }, 8, ["label", "error"]), + c(A, { + label: S(d)("tasks_projects.billing.preview.template"), + error: R.value.template_name, + required: "" + }, { + default: E(() => [c(U, { + modelValue: ue.value, + "onUpdate:modelValue": n[2] ||= (e) => ue.value = e, + options: le.value, + "label-key": "name" + }, null, 8, ["modelValue", "options"])]), + _: 1 + }, 8, ["label", "error"]), + be.value ? (g(), r(A, { + key: 0, + label: S(d)("tasks_projects.billing.preview.exchange_rate"), + error: R.value.exchange_rate, + required: "" + }, { + default: E(() => [c(te, { + modelValue: L.exchangeRate, + "onUpdate:modelValue": n[3] ||= (e) => L.exchangeRate = e, + type: "text", + name: "exchange_rate" + }, null, 8, ["modelValue"]), o("span", Yl, x(S(d)("tasks_projects.billing.preview.exchange_rate_help", { currency: ye.value?.code ?? "" })), 1)]), + _: 1 + }, 8, ["label", "error"])) : i("", !0) + ]), + o("div", Xl, [o("div", Zl, [o("table", Ql, [ + o("thead", $l, [o("tr", null, [ + o("th", eu, x(S(d)("tasks_projects.billing.preview.columns.description")), 1), + o("th", tu, x(S(d)("tasks_projects.billing.preview.columns.quantity")), 1), + o("th", nu, x(S(d)("tasks_projects.billing.preview.columns.price")), 1), + o("th", ru, x(S(d)("tasks_projects.billing.preview.columns.total")), 1) + ])]), + o("tbody", iu, [(g(!0), a(e, null, y(F.value.items, (e, t) => (g(), a("tr", { key: `${e.name}-${t}` }, [ + o("td", au, [o("span", ou, x(e.name), 1), e.description ? (g(), a("span", su, x(e.description), 1)) : i("", !0)]), + o("td", cu, x(e.quantity), 1), + o("td", lu, [c(N, { + amount: e.price, + currency: ye.value + }, null, 8, ["amount", "currency"])]), + o("td", uu, [c(N, { + amount: e.total, + currency: ye.value + }, null, 8, ["amount", "currency"])]) + ]))), 128))]), + o("tfoot", du, [o("tr", null, [o("td", fu, x(S(d)("tasks_projects.billing.preview.sub_total")), 1), o("td", pu, [c(N, { + amount: F.value.sub_total, + currency: ye.value + }, null, 8, ["amount", "currency"])])]), o("tr", null, [o("td", mu, x(S(d)("tasks_projects.billing.preview.total")), 1), o("td", hu, [c(N, { + amount: F.value.total, + currency: ye.value + }, null, 8, ["amount", "currency"])])])]) + ])])]), + o("div", gu, [c(_, { + variant: "primary-outline", + onClick: n[4] ||= (e) => p.value = 2 + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.back")), 1)]), + _: 1 + }), c(_, { + variant: "primary", + loading: B.value, + disabled: B.value, + onClick: Je + }, { + left: E((e) => [B.value ? i("", !0) : (g(), r(h, { + key: 0, + name: "DocumentPlusIcon", + class: f(e.class) + }, null, 8, ["class"]))]), + default: E(() => [s(" " + x(S(d)("tasks_projects.billing.preview.create")), 1)]), + _: 1 + }, 8, ["loading", "disabled"])]) + ], 64))])) : (g(), a("section", _u, [B.value || ne.value ? (g(), a("div", vu, [c(T, { class: "h-8 w-8 text-primary-500" }), o("p", yu, x(B.value ? S(d)("tasks_projects.billing.create.creating") : S(d)("tasks_projects.billing.create.stamping")), 1)])) : V.value ? (g(), a(e, { key: 1 }, [ee.value ? (g(), a("div", bu, [ + o("p", xu, x(S(d)("tasks_projects.billing.create.stamp_failed_title")), 1), + o("p", Su, x(S(d)("tasks_projects.billing.create.stamp_failed_description", { number: V.value.invoice_number })), 1), + o("div", Cu, [c(_, { + variant: "primary", + loading: ne.value, + onClick: Xe + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.create.retry_stamp")), 1)]), + _: 1 + }, 8, ["loading"]), c(v, { to: `${Au}/${V.value.id}/view` }, { + default: E(() => [c(_, { variant: "white" }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.create.view_invoice")), 1)]), + _: 1 + })]), + _: 1 + }, 8, ["to"])]) + ])) : (g(), a("div", wu, [ + c(h, { + name: "CheckCircleIcon", + class: "mx-auto h-12 w-12 text-primary-500" + }), + o("p", Tu, x(S(d)("tasks_projects.billing.create.created_title", { number: V.value.invoice_number })), 1), + o("p", Eu, x(S(d)("tasks_projects.billing.create.created_description", { count: H.value ?? 0 })), 1), + o("p", Du, [c(N, { + amount: V.value.total, + currency: ye.value + }, null, 8, ["amount", "currency"])]), + o("div", Ou, [c(v, { to: `${Au}/${V.value.id}/view` }, { + default: E(() => [c(_, { variant: "primary" }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.create.view_invoice")), 1)]), + _: 1 + })]), + _: 1 + }, 8, ["to"]), c(_, { + variant: "white", + onClick: Ze + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.create.invoice_more")), 1)]), + _: 1 + })]) + ]))], 64)) : (g(), a("div", ku, [c(_, { + variant: "primary-outline", + onClick: n[5] ||= (e) => p.value = 3 + }, { + default: E(() => [s(x(S(d)("tasks_projects.billing.back")), 1)]), + _: 1 + })]))])) + ]), + _: 1 + }); + }; + } +}), Mu = "tasks-projects"; +function Nu(e) { + e.addMessages(Fc), e.registerPage({ + id: "billing", + module: Mu, + path: "billing", + component: jc(e, ju), + meta: { + ability: `${Mu}:invoice-tasks`, + title: "tasks_projects.billing.title" + } + }); +} +//#endregion //#region resources/js/messages/reports.ts -var Fc = { en: { tasks_projects: { reports: { +var Pu = { en: { tasks_projects: { reports: { title: "Reports", load_failed: "Unable to load the report.", empty_title: "Nothing logged in this range", @@ -5939,68 +6807,68 @@ var Fc = { en: { tasks_projects: { reports: { amount: "Amount", unbilled: "Unbilled" } -} } } }, Ic = { summary: `${j}/reports/summary` }; -async function Lc(e, t) { - let { data: n } = await e.get(Ic.summary, { params: t }); - return Rc(n?.data, t); +} } } }, Fu = { summary: `${j}/reports/summary` }; +async function Iu(e, t) { + let { data: n } = await e.get(Fu.summary, { params: t }); + return Lu(n?.data, t); } -function Rc(e, t) { - let n = Vc(e) ? e : {}; +function Lu(e, t) { + let n = Bu(e) ? e : {}; return { - from: Wc(n.from, t.from ?? ""), - to: Wc(n.to, t.to ?? ""), - totals: Bc(n.totals).map(zc), - by_project: Bc(n.by_project).map((e) => ({ - ...zc(e), - project_id: Uc(e.project_id), - label: Wc(e.label, "") + from: Uu(n.from, t.from ?? ""), + to: Uu(n.to, t.to ?? ""), + totals: zu(n.totals).map(Ru), + by_project: zu(n.by_project).map((e) => ({ + ...Ru(e), + project_id: Hu(e.project_id), + label: Uu(e.label, "") })), - by_member: Bc(n.by_member).map((e) => ({ - ...zc(e), - user_id: Uc(e.user_id), - label: Wc(e.label, "") + by_member: zu(n.by_member).map((e) => ({ + ...Ru(e), + user_id: Hu(e.user_id), + label: Uu(e.label, "") })), - by_customer: Bc(n.by_customer).map((e) => ({ - ...zc(e), - customer_id: Uc(e.customer_id) + by_customer: zu(n.by_customer).map((e) => ({ + ...Ru(e), + customer_id: Hu(e.customer_id) })), - by_billable: Bc(n.by_billable).map((e) => ({ - ...zc(e), + by_billable: zu(n.by_billable).map((e) => ({ + ...Ru(e), billable: e.billable === !0 })) }; } -function zc(e) { +function Ru(e) { return { - currency_id: Uc(e.currency_id), - minutes: Hc(e.minutes), - amount: Hc(e.amount), - billable_minutes: Hc(e.billable_minutes), - billable_amount: Hc(e.billable_amount), - unbilled_amount: Hc(e.unbilled_amount) + currency_id: Hu(e.currency_id), + minutes: Vu(e.minutes), + amount: Vu(e.amount), + billable_minutes: Vu(e.billable_minutes), + billable_amount: Vu(e.billable_amount), + unbilled_amount: Vu(e.unbilled_amount) }; } -function Bc(e) { - return Array.isArray(e) ? e.filter(Vc) : []; +function zu(e) { + return Array.isArray(e) ? e.filter(Bu) : []; } -function Vc(e) { +function Bu(e) { return typeof e == "object" && !!e; } -function Hc(e) { +function Vu(e) { return typeof e == "number" && Number.isFinite(e) ? e : 0; } -function Uc(e) { +function Hu(e) { return typeof e == "number" && Number.isFinite(e) ? e : null; } -function Wc(e, t) { +function Uu(e, t) { return typeof e == "string" && e.trim() !== "" ? e : t; } //#endregion //#region resources/js/components/ReportBreakdownTable.vue?vue&type=script&setup=true&lang.ts -var Gc = { class: "mt-6" }, Kc = { class: "text-sm font-semibold tracking-wider text-muted uppercase" }, qc = { class: "relative table-container" }, Jc = { +var Wu = { class: "mt-6" }, Gu = { class: "text-sm font-semibold tracking-wider text-muted uppercase" }, Ku = { class: "relative table-container" }, qu = { key: 0, class: "text-subtle" -}, Yc = { key: 1 }, Xc = /* @__PURE__ */ l({ +}, Ju = { key: 1 }, Yu = /* @__PURE__ */ l({ __name: "ReportBreakdownTable", props: { title: {}, @@ -6009,7 +6877,7 @@ var Gc = { class: "mt-6" }, Kc = { class: "text-sm font-semibold tracking-wider showCurrency: { type: Boolean } }, setup(e) { - let t = e, i = H(), l = n(() => [ + let t = e, i = de(), l = n(() => [ { key: "label", label: t.labelHeading, @@ -6043,62 +6911,62 @@ var Gc = { class: "mt-6" }, Kc = { class: "text-sm font-semibold tracking-wider ]), u = n(() => t.showCurrency ? "currency" : "plain"); return (t, n) => { let i = b("BaseFormatMoney"), d = b("BaseTable"); - return g(), a("section", Gc, [o("h3", Kc, x(e.title), 1), o("div", qc, [(g(), r(d, { + return g(), a("section", Wu, [o("h3", Gu, x(e.title), 1), o("div", Ku, [(g(), r(d, { key: u.value, data: e.rows, columns: l.value, class: "mt-2" }, { - "cell-currency_id": E(({ row: e }) => [e.data.currency_id === null ? (g(), a("span", Jc, "-")) : (g(), a("span", Yc, "#" + x(e.data.currency_id), 1))]), - "cell-minutes": E(({ row: e }) => [s(x(S(wt)(e.data.minutes)), 1)]), - "cell-billable_minutes": E(({ row: e }) => [s(x(S(wt)(e.data.billable_minutes)), 1)]), + "cell-currency_id": E(({ row: e }) => [e.data.currency_id === null ? (g(), a("span", qu, "-")) : (g(), a("span", Ju, "#" + x(e.data.currency_id), 1))]), + "cell-minutes": E(({ row: e }) => [s(x(S(Ct)(e.data.minutes)), 1)]), + "cell-billable_minutes": E(({ row: e }) => [s(x(S(Ct)(e.data.billable_minutes)), 1)]), "cell-amount": E(({ row: e }) => [c(i, { amount: e.data.amount }, null, 8, ["amount"])]), "cell-unbilled_amount": E(({ row: e }) => [c(i, { amount: e.data.unbilled_amount }, null, 8, ["amount"])]), _: 1 }, 8, ["data", "columns"]))])]); }; } -}), Zc = 3; -function Qc(e, t, n = /* @__PURE__ */ new Date()) { +}), Xu = 3; +function Zu(e, t, n = /* @__PURE__ */ new Date()) { let r = n.getFullYear(), i = n.getMonth(); switch (e) { case "THIS_WEEK": { - let e = At(n, t); - return el(e, Mt(e, 6)); + let e = kt(n, t); + return $u(e, jt(e, 6)); } - case "LAST_MONTH": return el(new Date(r, i - 1, 1), new Date(r, i, 0)); + case "LAST_MONTH": return $u(new Date(r, i - 1, 1), new Date(r, i, 0)); case "THIS_QUARTER": { - let e = Math.floor(i / Zc) * Zc; - return el(new Date(r, e, 1), new Date(r, e + Zc, 0)); + let e = Math.floor(i / Xu) * Xu; + return $u(new Date(r, e, 1), new Date(r, e + Xu, 0)); } - case "THIS_YEAR": return el(new Date(r, 0, 1), new Date(r, 12, 0)); - default: return el(new Date(r, i, 1), new Date(r, i + 1, 0)); + case "THIS_YEAR": return $u(new Date(r, 0, 1), new Date(r, 12, 0)); + default: return $u(new Date(r, i, 1), new Date(r, i + 1, 0)); } } -function $c(e, t) { +function Qu(e, t) { return t <= 0 ? 0 : Math.min(100, Math.max(0, Math.round(e / t * 100))); } -function el(e, t) { +function $u(e, t) { return { - from: Nt(e), - to: Nt(t) + from: Mt(e), + to: Mt(t) }; } //#endregion //#region resources/js/pages/ReportsPage.vue?vue&type=script&setup=true&lang.ts -var tl = { +var ed = { key: 0, class: "mt-2 text-sm text-muted" -}, nl = { class: "flex items-center justify-end space-x-5" }, rl = { class: "mt-4 flex flex-wrap gap-2" }, il = ["onClick"], al = { +}, td = { class: "flex items-center justify-end space-x-5" }, nd = { class: "mt-4 flex flex-wrap gap-2" }, rd = ["onClick"], id = { key: 0, class: "flex justify-center py-16" -}, ol = { +}, ad = { key: 0, class: "text-xs font-medium tracking-wider text-muted uppercase" -}, sl = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, cl = { class: "mt-1 text-2xl font-semibold text-heading" }, ll = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, ul = { class: "mt-1 text-2xl font-semibold text-heading" }, dl = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, fl = { class: "mt-1 text-2xl font-semibold text-heading" }, pl = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, ml = { class: "mt-1 text-2xl font-semibold text-heading" }, hl = { class: "mt-4 rounded-xl border border-line-default bg-surface p-5" }, gl = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, _l = { class: "mt-3 flex h-2 w-full overflow-hidden rounded-full bg-surface-tertiary" }, vl = { class: "mt-3 flex flex-wrap gap-6 text-sm" }, yl = { class: "inline-flex items-center text-body" }, bl = { class: "ml-1 font-medium text-heading" }, xl = { class: "ml-1 text-muted" }, Sl = { class: "inline-flex items-center text-body" }, Cl = { class: "ml-1 font-medium text-heading" }, wl = { class: "ml-1 text-muted" }, Tl = { +}, od = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, sd = { class: "mt-1 text-2xl font-semibold text-heading" }, cd = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, ld = { class: "mt-1 text-2xl font-semibold text-heading" }, ud = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, dd = { class: "mt-1 text-2xl font-semibold text-heading" }, fd = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, pd = { class: "mt-1 text-2xl font-semibold text-heading" }, md = { class: "mt-4 rounded-xl border border-line-default bg-surface p-5" }, hd = { class: "text-xs font-medium tracking-wider text-muted uppercase" }, gd = { class: "mt-3 flex h-2 w-full overflow-hidden rounded-full bg-surface-tertiary" }, _d = { class: "mt-3 flex flex-wrap gap-6 text-sm" }, vd = { class: "inline-flex items-center text-body" }, yd = { class: "ml-1 font-medium text-heading" }, bd = { class: "ml-1 text-muted" }, xd = { class: "inline-flex items-center text-body" }, Sd = { class: "ml-1 font-medium text-heading" }, Cd = { class: "ml-1 text-muted" }, wd = { key: 1, class: "mt-2 text-sm text-subtle" -}, El = "THIS_MONTH", Dl = /* @__PURE__ */ l({ +}, Td = "THIS_MONTH", Ed = /* @__PURE__ */ l({ __name: "ReportsPage", props: { client: { type: [Function, Object] }, @@ -6106,7 +6974,7 @@ var tl = { router: {} }, setup(t) { - let l = t, u = H(), d = v(null), m = v([]), _ = v(!0), C = v(El), w = v(""), T = v(""), D = n(() => [ + let l = t, u = de(), d = v(null), m = v([]), _ = v(!0), C = v(Td), w = v(""), T = v(""), D = n(() => [ { id: "THIS_WEEK", label: u("tasks_projects.reports.range.this_week") @@ -6138,70 +7006,70 @@ var tl = { }))), M = n(() => (d.value?.by_member ?? []).map((e) => ({ ...e, id: `${e.user_id ?? "none"}-${e.currency_id ?? "base"}`, - label: ie(e) + label: ne(e) }))), N = n(() => (d.value?.by_customer ?? []).map((e) => ({ ...e, id: `${e.customer_id ?? "none"}-${e.currency_id ?? "base"}`, - label: e.customer_id === null ? u("tasks_projects.reports.tables.no_customer") : Se(e.customer_id) - }))), P = n(() => ae(!0)), F = n(() => ae(!1)), I = n(() => P.value + F.value), L = n(() => $c(P.value, I.value)); + label: e.customer_id === null ? u("tasks_projects.reports.tables.no_customer") : xe(e.customer_id) + }))), P = n(() => re(!0)), F = n(() => re(!1)), I = n(() => P.value + F.value), L = n(() => Qu(P.value, I.value)); h(() => { - ee(El), re(); + R(Td), te(); }); - function ee(e) { + function R(e) { if (C.value = e, e !== "CUSTOM") { - let t = Qc(e, pr.settings.week_start); + let t = Zu(e, fr.settings.week_start); w.value = t.from, T.value = t.to; } - B(); - } - function R(e) { - w.value = e ? le(e) : "", C.value = "CUSTOM", B(); + H(); } function z(e) { - T.value = e ? le(e) : "", C.value = "CUSTOM", B(); + w.value = e ? se(e) : "", C.value = "CUSTOM", H(); } - async function B() { + function B(e) { + T.value = e ? se(e) : "", C.value = "CUSTOM", H(); + } + async function H() { _.value = !0; try { - let e = await Lc(l.client, ne()); - d.value = e, e.by_customer.some((e) => e.customer_id !== null) && Ce(l.client); + let e = await Iu(l.client, ee()); + d.value = e, e.by_customer.some((e) => e.customer_id !== null) && Se(l.client); } catch (e) { - d.value = null, l.notify("error", V(e, u("tasks_projects.reports.load_failed"))); + d.value = null, l.notify("error", U(e, u("tasks_projects.reports.load_failed"))); } finally { _.value = !1; } } - function ne() { + function ee() { let e = {}; return w.value !== "" && (e.from = w.value), T.value !== "" && (e.to = T.value), e; } - async function re() { + async function te() { try { - m.value = await te(l.client); + m.value = await V(l.client); } catch {} } - function ie(e) { + function ne(e) { let t = m.value.find((t) => t.id === e.user_id)?.name ?? ""; return t === "" ? e.label === "" ? e.user_id === null ? u("tasks_projects.reports.tables.unknown_member") : `#${e.user_id}` : e.label : t; } - function ae(e) { + function re(e) { return (d.value?.by_billable ?? []).filter((t) => t.billable === e).reduce((e, t) => e + t.minutes, 0); } - function oe(e) { + function ie(e) { return e === null ? u("tasks_projects.reports.summary.base_currency") : u("tasks_projects.reports.summary.currency", { id: e }); } - function se(e) { + function ae(e) { return C.value === e.id ? "border-primary-500 bg-primary-50 text-primary-500" : "border-line-default bg-surface text-muted hover:text-heading"; } - function ue() { - ee(El); + function ce() { + R(Td); } return (t, n) => { - let l = b("BaseBreadcrumbItem"), m = b("BaseBreadcrumb"), h = b("BaseIcon"), v = b("BaseButton"), C = b("router-link"), te = b("BasePageHeader"), B = b("BaseDatePicker"), ne = b("BaseInputGroup"), V = b("BaseFilterWrapper"), re = b("BaseSpinner"), ie = b("BaseEmptyPlaceholder"), ae = b("BaseFormatMoney"), le = b("BasePage"); - return g(), r(le, null, { + let l = b("BaseBreadcrumbItem"), m = b("BaseBreadcrumb"), h = b("BaseIcon"), v = b("BaseButton"), C = b("router-link"), V = b("BasePageHeader"), H = b("BaseDatePicker"), ee = b("BaseInputGroup"), U = b("BaseFilterWrapper"), te = b("BaseSpinner"), ne = b("BaseEmptyPlaceholder"), re = b("BaseFormatMoney"), se = b("BasePage"); + return g(), r(se, null, { default: E(() => [ - c(te, { title: S(u)("tasks_projects.reports.title") }, { - actions: E(() => [o("div", nl, [c(C, { to: "/admin/modules/tasks-projects" }, { + c(V, { title: S(u)("tasks_projects.reports.title") }, { + actions: E(() => [o("div", td, [c(C, { to: "/admin/modules/tasks-projects" }, { default: E(() => [c(v, { variant: "white" }, { left: E((e) => [c(h, { name: "FolderIcon", @@ -6239,67 +7107,67 @@ var tl = { }, null, 8, ["title"]) ]), _: 1 - }), d.value ? (g(), a("p", tl, x(S(ce)(d.value.from)) + " – " + x(S(ce)(d.value.to)), 1)) : i("", !0)]), + }), d.value ? (g(), a("p", ed, x(S(oe)(d.value.from)) + " – " + x(S(oe)(d.value.to)), 1)) : i("", !0)]), _: 1 }, 8, ["title"]), - o("div", rl, [(g(!0), a(e, null, y(D.value, (e) => (g(), a("button", { + o("div", nd, [(g(!0), a(e, null, y(D.value, (e) => (g(), a("button", { key: e.id, type: "button", - class: f(["rounded-md border px-3 py-1.5 text-sm font-medium", se(e)]), - onClick: (t) => ee(e.id) - }, x(e.label), 11, il))), 128))]), - c(V, { + class: f(["rounded-md border px-3 py-1.5 text-sm font-medium", ae(e)]), + onClick: (t) => R(e.id) + }, x(e.label), 11, rd))), 128))]), + c(U, { show: !0, "row-on-xl": "", class: "mt-3", - onClear: ue + onClear: ce }, { - default: E(() => [c(ne, { + default: E(() => [c(ee, { label: S(u)("tasks_projects.reports.range.from"), class: "mt-2 flex-1" }, { - default: E(() => [c(B, { + default: E(() => [c(H, { "model-value": w.value, - "onUpdate:modelValue": R + "onUpdate:modelValue": z }, null, 8, ["model-value"])]), _: 1 - }, 8, ["label"]), c(ne, { + }, 8, ["label"]), c(ee, { label: S(u)("tasks_projects.reports.range.to"), class: "mt-2 flex-1" }, { - default: E(() => [c(B, { + default: E(() => [c(H, { "model-value": T.value, - "onUpdate:modelValue": z + "onUpdate:modelValue": B }, null, 8, ["model-value"])]), _: 1 }, 8, ["label"])]), _: 1 }), - _.value && d.value === null ? (g(), a("div", al, [c(re, { class: "h-8 w-8 text-primary-500" })])) : A.value ? (g(), a(e, { key: 2 }, [ + _.value && d.value === null ? (g(), a("div", id, [c(te, { class: "h-8 w-8 text-primary-500" })])) : A.value ? (g(), a(e, { key: 2 }, [ (g(!0), a(e, null, y(O.value, (e) => (g(), a("div", { key: e.currency_id ?? "base", class: "mt-4 rounded-xl border border-line-default bg-surface p-5" - }, [k.value ? (g(), a("p", ol, x(oe(e.currency_id)), 1)) : i("", !0), o("div", { class: f(["grid grid-cols-2 gap-4 sm:grid-cols-4", k.value ? "mt-3" : ""]) }, [ - o("div", null, [o("p", sl, x(S(u)("tasks_projects.reports.summary.logged")), 1), o("p", cl, x(S(wt)(e.minutes)), 1)]), - o("div", null, [o("p", ll, x(S(u)("tasks_projects.reports.summary.billable")), 1), o("p", ul, x(S(wt)(e.billable_minutes)), 1)]), - o("div", null, [o("p", dl, x(S(u)("tasks_projects.reports.summary.amount")), 1), o("p", fl, [c(ae, { amount: e.amount }, null, 8, ["amount"])])]), - o("div", null, [o("p", pl, x(S(u)("tasks_projects.reports.summary.unbilled")), 1), o("p", ml, [c(ae, { amount: e.unbilled_amount }, null, 8, ["amount"])])]) + }, [k.value ? (g(), a("p", ad, x(ie(e.currency_id)), 1)) : i("", !0), o("div", { class: f(["grid grid-cols-2 gap-4 sm:grid-cols-4", k.value ? "mt-3" : ""]) }, [ + o("div", null, [o("p", od, x(S(u)("tasks_projects.reports.summary.logged")), 1), o("p", sd, x(S(Ct)(e.minutes)), 1)]), + o("div", null, [o("p", cd, x(S(u)("tasks_projects.reports.summary.billable")), 1), o("p", ld, x(S(Ct)(e.billable_minutes)), 1)]), + o("div", null, [o("p", ud, x(S(u)("tasks_projects.reports.summary.amount")), 1), o("p", dd, [c(re, { amount: e.amount }, null, 8, ["amount"])])]), + o("div", null, [o("p", fd, x(S(u)("tasks_projects.reports.summary.unbilled")), 1), o("p", pd, [c(re, { amount: e.unbilled_amount }, null, 8, ["amount"])])]) ], 2)]))), 128)), - o("section", hl, [o("p", gl, x(S(u)("tasks_projects.reports.split.title")), 1), I.value > 0 ? (g(), a(e, { key: 0 }, [o("div", _l, [o("div", { + o("section", md, [o("p", hd, x(S(u)("tasks_projects.reports.split.title")), 1), I.value > 0 ? (g(), a(e, { key: 0 }, [o("div", gd, [o("div", { class: "h-2 bg-primary-500", style: p({ width: `${L.value}%` }) - }, null, 4)]), o("div", vl, [o("span", yl, [ + }, null, 4)]), o("div", _d, [o("span", vd, [ n[0] ||= o("span", { class: "mr-2 inline-block h-2.5 w-2.5 rounded-full bg-primary-500" }, null, -1), s(" " + x(S(u)("tasks_projects.reports.split.billable")) + ": ", 1), - o("span", bl, x(S(wt)(P.value)), 1), - o("span", xl, "(" + x(L.value) + "%)", 1) - ]), o("span", Sl, [ + o("span", yd, x(S(Ct)(P.value)), 1), + o("span", bd, "(" + x(L.value) + "%)", 1) + ]), o("span", xd, [ n[1] ||= o("span", { class: "mr-2 inline-block h-2.5 w-2.5 rounded-full bg-surface-tertiary" }, null, -1), s(" " + x(S(u)("tasks_projects.reports.split.non_billable")) + ": ", 1), - o("span", Cl, x(S(wt)(F.value)), 1), - o("span", wl, "(" + x(100 - L.value) + "%)", 1) - ])])], 64)) : (g(), a("p", Tl, x(S(u)("tasks_projects.reports.split.nothing")), 1))]), - c(Xc, { + o("span", Sd, x(S(Ct)(F.value)), 1), + o("span", Cd, "(" + x(100 - L.value) + "%)", 1) + ])])], 64)) : (g(), a("p", wd, x(S(u)("tasks_projects.reports.split.nothing")), 1))]), + c(Yu, { title: S(u)("tasks_projects.reports.tables.by_project"), "label-heading": S(u)("tasks_projects.reports.tables.project"), rows: j.value, @@ -6310,7 +7178,7 @@ var tl = { "rows", "show-currency" ]), - c(Xc, { + c(Yu, { title: S(u)("tasks_projects.reports.tables.by_member"), "label-heading": S(u)("tasks_projects.reports.tables.member"), rows: M.value, @@ -6321,7 +7189,7 @@ var tl = { "rows", "show-currency" ]), - c(Xc, { + c(Yu, { title: S(u)("tasks_projects.reports.tables.by_customer"), "label-heading": S(u)("tasks_projects.reports.tables.customer"), rows: N.value, @@ -6332,7 +7200,7 @@ var tl = { "rows", "show-currency" ]) - ], 64)) : (g(), r(ie, { + ], 64)) : (g(), r(ne, { key: 1, title: S(u)("tasks_projects.reports.empty_title"), description: S(u)("tasks_projects.reports.empty_description") @@ -6348,37 +7216,37 @@ var tl = { }); }; } -}), Ol = "tasks-projects"; -function kl(e) { - e.addMessages(Fc), e.registerPage({ +}), Dd = "tasks-projects"; +function Od(e) { + e.addMessages(Pu), e.registerPage({ id: "reports", - module: Ol, + module: Dd, path: "reports", - component: jc(e, Dl), + component: jc(e, Ed), meta: { - ability: `${Ol}:view-own-time`, + ability: `${Dd}:view-own-time`, title: "tasks_projects.reports.title" } }), e.on("company:changing", () => { - we(); + Ce(); }); } //#endregion //#region resources/js/init.ts -var Al = "tasks-projects"; +var kd = "tasks-projects"; window.InvoiceShelf.booting((e, t, n) => { n.addMessages(A), n.registerPage({ id: "projects", - module: Al, + module: kd, path: "", - component: jl(n, Re), + component: Ad(n, Le), meta: { - ability: `${Al}:view-project`, + ability: `${kd}:view-project`, title: "tasks_projects.projects.title" } - }), ri(n), Pc(n), kl(n); + }), ni(n), Pc(n), Nu(n), Od(n); }); -function jl(e, t) { +function Ad(e, t) { return l({ setup: (n, { attrs: r }) => () => d(t, { ...r, client: e.client, diff --git a/dist/style.css b/dist/style.css index 397e0f4..70a0446 100644 --- a/dist/style.css +++ b/dist/style.css @@ -1,3 +1,3 @@ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.fixed{position:fixed}.relative{position:relative}.right-6{right:calc(var(--spacing,.25rem) * 6)}.bottom-20{bottom:calc(var(--spacing,.25rem) * 20)}.z-40{z-index:40}.float-left{float:left}.m-0{margin:0}.mt-1{margin-top:var(--spacing,.25rem)}.mt-2{margin-top:calc(var(--spacing,.25rem) * 2)}.mt-3{margin-top:calc(var(--spacing,.25rem) * 3)}.mt-4{margin-top:calc(var(--spacing,.25rem) * 4)}.mt-5{margin-top:calc(var(--spacing,.25rem) * 5)}.mt-6{margin-top:calc(var(--spacing,.25rem) * 6)}.mr-0\.5{margin-right:calc(var(--spacing,.25rem) * .5)}.mr-2{margin-right:calc(var(--spacing,.25rem) * 2)}.mr-3{margin-right:calc(var(--spacing,.25rem) * 3)}.-mb-px{margin-bottom:-1px}.mb-4{margin-bottom:calc(var(--spacing,.25rem) * 4)}.ml-1{margin-left:var(--spacing,.25rem)}.ml-2{margin-left:calc(var(--spacing,.25rem) * 2)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1\.5{height:calc(var(--spacing,.25rem) * 1.5)}.h-2{height:calc(var(--spacing,.25rem) * 2)}.h-2\.5{height:calc(var(--spacing,.25rem) * 2.5)}.h-3{height:calc(var(--spacing,.25rem) * 3)}.h-3\.5{height:calc(var(--spacing,.25rem) * 3.5)}.h-4{height:calc(var(--spacing,.25rem) * 4)}.h-5{height:calc(var(--spacing,.25rem) * 5)}.h-6{height:calc(var(--spacing,.25rem) * 6)}.h-7{height:calc(var(--spacing,.25rem) * 7)}.h-8{height:calc(var(--spacing,.25rem) * 8)}.h-16{height:calc(var(--spacing,.25rem) * 16)}.max-h-48{max-height:calc(var(--spacing,.25rem) * 48)}.min-h-40{min-height:calc(var(--spacing,.25rem) * 40)}.min-h-\[80px\]{min-height:80px}.w-1\.5{width:calc(var(--spacing,.25rem) * 1.5)}.w-2{width:calc(var(--spacing,.25rem) * 2)}.w-2\.5{width:calc(var(--spacing,.25rem) * 2.5)}.w-3{width:calc(var(--spacing,.25rem) * 3)}.w-3\.5{width:calc(var(--spacing,.25rem) * 3.5)}.w-4{width:calc(var(--spacing,.25rem) * 4)}.w-5{width:calc(var(--spacing,.25rem) * 5)}.w-6{width:calc(var(--spacing,.25rem) * 6)}.w-7{width:calc(var(--spacing,.25rem) * 7)}.w-8{width:calc(var(--spacing,.25rem) * 8)}.w-16{width:calc(var(--spacing,.25rem) * 16)}.w-72{width:calc(var(--spacing,.25rem) * 72)}.w-80{width:calc(var(--spacing,.25rem) * 80)}.w-full{width:100%}.max-w-32{max-width:calc(var(--spacing,.25rem) * 32)}.max-w-64{max-width:calc(var(--spacing,.25rem) * 64)}.max-w-\[calc\(100vw-3rem\)\]{max-width:calc(100vw - 3rem)}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse,pulse 2s cubic-bezier(.4, 0, .6, 1) infinite)}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing,.25rem)}.gap-2{gap:calc(var(--spacing,.25rem) * 2)}.gap-3{gap:calc(var(--spacing,.25rem) * 3)}.gap-4{gap:calc(var(--spacing,.25rem) * 4)}.gap-6{gap:calc(var(--spacing,.25rem) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing,.25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing,.25rem) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing,.25rem) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing,.25rem) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-5>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing,.25rem) * 5) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing,.25rem) * 5) * calc(1 - var(--tw-space-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-line-light>:not(:last-child)){border-color:var(--color-line-light)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg,.5rem)}.rounded-md{border-radius:var(--radius-md,.375rem)}.rounded-sm{border-radius:var(--radius-sm,.25rem)}.rounded-xl{border-radius:var(--radius-xl,.75rem)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-heading{border-color:var(--color-heading)}.border-line-default{border-color:var(--color-line-default)}.border-line-light{border-color:var(--color-line-light)}.border-primary-400{border-color:var(--color-primary-400)}.border-primary-500{border-color:var(--color-primary-500)}.border-transparent{border-color:#0000}.bg-alert-error-bg{background-color:var(--color-alert-error-bg)}.bg-alert-warning-bg{background-color:var(--color-alert-warning-bg)}.bg-btn-primary{background-color:var(--color-btn-primary)}.bg-hover-strong{background-color:var(--color-hover-strong)}.bg-line-default{background-color:var(--color-line-default)}.bg-line-strong{background-color:var(--color-line-strong)}.bg-primary-50{background-color:var(--color-primary-50)}.bg-primary-50\!{background-color:var(--color-primary-50)!important}.bg-primary-500{background-color:var(--color-primary-500)}.bg-status-green{background-color:var(--color-status-green)}.bg-status-red{background-color:var(--color-status-red)}.bg-surface{background-color:var(--color-surface)}.bg-surface-secondary{background-color:var(--color-surface-secondary)}.bg-surface-tertiary{background-color:var(--color-surface-tertiary)}.bg-surface-tertiary\!{background-color:var(--color-surface-tertiary)!important}.bg-white{background-color:var(--color-white,#fff)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab, red, red)){.bg-white\/20{background-color:color-mix(in oklab, var(--color-white,#fff) 20%, transparent)}}.p-1{padding:var(--spacing,.25rem)}.p-3{padding:calc(var(--spacing,.25rem) * 3)}.p-5{padding:calc(var(--spacing,.25rem) * 5)}.px-1{padding-inline:var(--spacing,.25rem)}.px-1\.5{padding-inline:calc(var(--spacing,.25rem) * 1.5)}.px-2{padding-inline:calc(var(--spacing,.25rem) * 2)}.px-3{padding-inline:calc(var(--spacing,.25rem) * 3)}.px-4{padding-inline:calc(var(--spacing,.25rem) * 4)}.px-5{padding-inline:calc(var(--spacing,.25rem) * 5)}.px-6{padding-inline:calc(var(--spacing,.25rem) * 6)}.py-0\.5{padding-block:calc(var(--spacing,.25rem) * .5)}.py-1{padding-block:var(--spacing,.25rem)}.py-1\.5{padding-block:calc(var(--spacing,.25rem) * 1.5)}.py-2{padding-block:calc(var(--spacing,.25rem) * 2)}.py-2\.5{padding-block:calc(var(--spacing,.25rem) * 2.5)}.py-3{padding-block:calc(var(--spacing,.25rem) * 3)}.py-4{padding-block:calc(var(--spacing,.25rem) * 4)}.py-6{padding-block:calc(var(--spacing,.25rem) * 6)}.py-8{padding-block:calc(var(--spacing,.25rem) * 8)}.py-10{padding-block:calc(var(--spacing,.25rem) * 10)}.py-16{padding-block:calc(var(--spacing,.25rem) * 16)}.pt-2{padding-top:calc(var(--spacing,.25rem) * 2)}.pt-3{padding-top:calc(var(--spacing,.25rem) * 3)}.pb-3{padding-bottom:calc(var(--spacing,.25rem) * 3)}.pb-4{padding-bottom:calc(var(--spacing,.25rem) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl,1.5rem);line-height:var(--tw-leading,var(--text-2xl--line-height,calc(2 / 1.5)))}.text-lg{font-size:var(--text-lg,1.125rem);line-height:var(--tw-leading,var(--text-lg--line-height,calc(1.75 / 1.125)))}.text-sm{font-size:var(--text-sm,.875rem);line-height:var(--tw-leading,var(--text-sm--line-height,calc(1.25 / .875)))}.text-xs{font-size:var(--text-xs,.75rem);line-height:var(--tw-leading,var(--text-xs--line-height,calc(1 / .75)))}.text-\[11px\]{font-size:11px}.leading-5{--tw-leading:calc(var(--spacing,.25rem) * 5);line-height:calc(var(--spacing,.25rem) * 5)}.font-medium{--tw-font-weight:var(--font-weight-medium,500);font-weight:var(--font-weight-medium,500)}.font-normal{--tw-font-weight:var(--font-weight-normal,400);font-weight:var(--font-weight-normal,400)}.font-semibold{--tw-font-weight:var(--font-weight-semibold,600);font-weight:var(--font-weight-semibold,600)}.tracking-wide{--tw-tracking:var(--tracking-wide,.025em);letter-spacing:var(--tracking-wide,.025em)}.tracking-wider{--tw-tracking:var(--tracking-wider,.05em);letter-spacing:var(--tracking-wider,.05em)}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.text-alert-error-text{color:var(--color-alert-error-text)}.text-alert-warning-text{color:var(--color-alert-warning-text)}.text-body{color:var(--color-body)}.text-heading{color:var(--color-heading)}.text-muted{color:var(--color-muted)}.text-muted\!{color:var(--color-muted)!important}.text-primary-500{color:var(--color-primary-500)}.text-primary-500\!{color:var(--color-primary-500)!important}.text-status-green{color:var(--color-status-green)}.text-status-red{color:var(--color-status-red)}.text-subtle{color:var(--color-subtle)}.text-white{color:var(--color-white,#fff)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-40{opacity:.4}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function,cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration,var(--default-transition-duration,.15s))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function,cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration,var(--default-transition-duration,.15s))}@media (hover:hover){.group-hover\:text-muted:is(:where(.group):hover *){color:var(--color-muted)}.hover\:border-line-strong:hover{border-color:var(--color-line-strong)}.hover\:bg-btn-primary-hover:hover{background-color:var(--color-btn-primary-hover)}.hover\:bg-hover:hover{background-color:var(--color-hover)}.hover\:bg-white\/20:hover{background-color:#fff3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-white\/20:hover{background-color:color-mix(in oklab, var(--color-white,#fff) 20%, transparent)}}.hover\:text-alert-error-text:hover{color:var(--color-alert-error-text)}.hover\:text-body:hover{color:var(--color-body)}.hover\:text-heading:hover{color:var(--color-heading)}.hover\:text-primary-500:hover{color:var(--color-primary-500)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-primary-400:focus{border-color:var(--color-primary-400)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary-400:focus{--tw-ring-color:var(--color-primary-400)}.focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (width>=48rem){.md\:h-9{height:calc(var(--spacing,.25rem) * 9)}.md\:px-3{padding-inline:calc(var(--spacing,.25rem) * 3)}}@media (width>=64rem){.lg\:mb-1{margin-bottom:var(--spacing,.25rem)}.lg\:block{display:block}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-end{align-items:flex-end}}@media (width>=80rem){.xl\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.fixed{position:fixed}.relative{position:relative}.right-6{right:calc(var(--spacing,.25rem) * 6)}.bottom-20{bottom:calc(var(--spacing,.25rem) * 20)}.z-40{z-index:40}.float-left{float:left}.m-0{margin:0}.mx-auto{margin-inline:auto}.mt-1{margin-top:var(--spacing,.25rem)}.mt-2{margin-top:calc(var(--spacing,.25rem) * 2)}.mt-3{margin-top:calc(var(--spacing,.25rem) * 3)}.mt-4{margin-top:calc(var(--spacing,.25rem) * 4)}.mt-5{margin-top:calc(var(--spacing,.25rem) * 5)}.mt-6{margin-top:calc(var(--spacing,.25rem) * 6)}.mr-0\.5{margin-right:calc(var(--spacing,.25rem) * .5)}.mr-2{margin-right:calc(var(--spacing,.25rem) * 2)}.mr-3{margin-right:calc(var(--spacing,.25rem) * 3)}.-mb-px{margin-bottom:-1px}.mb-4{margin-bottom:calc(var(--spacing,.25rem) * 4)}.ml-1{margin-left:var(--spacing,.25rem)}.ml-2{margin-left:calc(var(--spacing,.25rem) * 2)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.h-1\.5{height:calc(var(--spacing,.25rem) * 1.5)}.h-2{height:calc(var(--spacing,.25rem) * 2)}.h-2\.5{height:calc(var(--spacing,.25rem) * 2.5)}.h-3{height:calc(var(--spacing,.25rem) * 3)}.h-3\.5{height:calc(var(--spacing,.25rem) * 3.5)}.h-4{height:calc(var(--spacing,.25rem) * 4)}.h-5{height:calc(var(--spacing,.25rem) * 5)}.h-6{height:calc(var(--spacing,.25rem) * 6)}.h-7{height:calc(var(--spacing,.25rem) * 7)}.h-8{height:calc(var(--spacing,.25rem) * 8)}.h-12{height:calc(var(--spacing,.25rem) * 12)}.h-16{height:calc(var(--spacing,.25rem) * 16)}.max-h-48{max-height:calc(var(--spacing,.25rem) * 48)}.min-h-40{min-height:calc(var(--spacing,.25rem) * 40)}.min-h-\[80px\]{min-height:80px}.w-1\.5{width:calc(var(--spacing,.25rem) * 1.5)}.w-2{width:calc(var(--spacing,.25rem) * 2)}.w-2\.5{width:calc(var(--spacing,.25rem) * 2.5)}.w-3{width:calc(var(--spacing,.25rem) * 3)}.w-3\.5{width:calc(var(--spacing,.25rem) * 3.5)}.w-4{width:calc(var(--spacing,.25rem) * 4)}.w-5{width:calc(var(--spacing,.25rem) * 5)}.w-6{width:calc(var(--spacing,.25rem) * 6)}.w-7{width:calc(var(--spacing,.25rem) * 7)}.w-8{width:calc(var(--spacing,.25rem) * 8)}.w-10{width:calc(var(--spacing,.25rem) * 10)}.w-12{width:calc(var(--spacing,.25rem) * 12)}.w-16{width:calc(var(--spacing,.25rem) * 16)}.w-72{width:calc(var(--spacing,.25rem) * 72)}.w-80{width:calc(var(--spacing,.25rem) * 80)}.w-full{width:100%}.max-w-32{max-width:calc(var(--spacing,.25rem) * 32)}.max-w-64{max-width:calc(var(--spacing,.25rem) * 64)}.max-w-\[calc\(100vw-3rem\)\]{max-width:calc(100vw - 3rem)}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.table-auto{table-layout:auto}.animate-pulse{animation:var(--animate-pulse,pulse 2s cubic-bezier(.4, 0, .6, 1) infinite)}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:var(--spacing,.25rem)}.gap-2{gap:calc(var(--spacing,.25rem) * 2)}.gap-3{gap:calc(var(--spacing,.25rem) * 3)}.gap-4{gap:calc(var(--spacing,.25rem) * 4)}.gap-6{gap:calc(var(--spacing,.25rem) * 6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing,.25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing,.25rem) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 6) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-6{column-gap:calc(var(--spacing,.25rem) * 6)}:where(.space-x-3>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing,.25rem) * 3) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing,.25rem) * 3) * calc(1 - var(--tw-space-x-reverse)))}:where(.space-x-5>:not(:last-child)){--tw-space-x-reverse:0;margin-inline-start:calc(calc(var(--spacing,.25rem) * 5) * var(--tw-space-x-reverse));margin-inline-end:calc(calc(var(--spacing,.25rem) * 5) * calc(1 - var(--tw-space-x-reverse)))}.gap-y-3{row-gap:calc(var(--spacing,.25rem) * 3)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-line-default>:not(:last-child)){border-color:var(--color-line-default)}:where(.divide-line-light>:not(:last-child)){border-color:var(--color-line-light)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:2147483647px}.rounded-lg{border-radius:var(--radius-lg,.5rem)}.rounded-md{border-radius:var(--radius-md,.375rem)}.rounded-sm{border-radius:var(--radius-sm,.25rem)}.rounded-xl{border-radius:var(--radius-xl,.75rem)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-heading{border-color:var(--color-heading)}.border-line-default{border-color:var(--color-line-default)}.border-line-light{border-color:var(--color-line-light)}.border-line-strong{border-color:var(--color-line-strong)}.border-primary-400{border-color:var(--color-primary-400)}.border-primary-500{border-color:var(--color-primary-500)}.border-status-red{border-color:var(--color-status-red)}.border-status-yellow{border-color:var(--color-status-yellow)}.border-transparent{border-color:#0000}.bg-alert-error-bg{background-color:var(--color-alert-error-bg)}.bg-alert-warning-bg{background-color:var(--color-alert-warning-bg)}.bg-btn-primary{background-color:var(--color-btn-primary)}.bg-hover-strong{background-color:var(--color-hover-strong)}.bg-line-default{background-color:var(--color-line-default)}.bg-line-strong{background-color:var(--color-line-strong)}.bg-primary-50{background-color:var(--color-primary-50)}.bg-primary-50\!{background-color:var(--color-primary-50)!important}.bg-primary-500{background-color:var(--color-primary-500)}.bg-status-green{background-color:var(--color-status-green)}.bg-status-red{background-color:var(--color-status-red)}.bg-surface{background-color:var(--color-surface)}.bg-surface-secondary{background-color:var(--color-surface-secondary)}.bg-surface-tertiary{background-color:var(--color-surface-tertiary)}.bg-surface-tertiary\!{background-color:var(--color-surface-tertiary)!important}.bg-white{background-color:var(--color-white,#fff)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab, red, red)){.bg-white\/20{background-color:color-mix(in oklab, var(--color-white,#fff) 20%, transparent)}}.p-1{padding:var(--spacing,.25rem)}.p-3{padding:calc(var(--spacing,.25rem) * 3)}.p-5{padding:calc(var(--spacing,.25rem) * 5)}.p-6{padding:calc(var(--spacing,.25rem) * 6)}.px-1{padding-inline:var(--spacing,.25rem)}.px-1\.5{padding-inline:calc(var(--spacing,.25rem) * 1.5)}.px-2{padding-inline:calc(var(--spacing,.25rem) * 2)}.px-3{padding-inline:calc(var(--spacing,.25rem) * 3)}.px-4{padding-inline:calc(var(--spacing,.25rem) * 4)}.px-5{padding-inline:calc(var(--spacing,.25rem) * 5)}.px-6{padding-inline:calc(var(--spacing,.25rem) * 6)}.py-0\.5{padding-block:calc(var(--spacing,.25rem) * .5)}.py-1{padding-block:var(--spacing,.25rem)}.py-1\.5{padding-block:calc(var(--spacing,.25rem) * 1.5)}.py-2{padding-block:calc(var(--spacing,.25rem) * 2)}.py-2\.5{padding-block:calc(var(--spacing,.25rem) * 2.5)}.py-3{padding-block:calc(var(--spacing,.25rem) * 3)}.py-4{padding-block:calc(var(--spacing,.25rem) * 4)}.py-6{padding-block:calc(var(--spacing,.25rem) * 6)}.py-8{padding-block:calc(var(--spacing,.25rem) * 8)}.py-10{padding-block:calc(var(--spacing,.25rem) * 10)}.py-16{padding-block:calc(var(--spacing,.25rem) * 16)}.pt-2{padding-top:calc(var(--spacing,.25rem) * 2)}.pt-3{padding-top:calc(var(--spacing,.25rem) * 3)}.pb-3{padding-bottom:calc(var(--spacing,.25rem) * 3)}.pb-4{padding-bottom:calc(var(--spacing,.25rem) * 4)}.pl-4{padding-left:calc(var(--spacing,.25rem) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl,1.5rem);line-height:var(--tw-leading,var(--text-2xl--line-height,calc(2 / 1.5)))}.text-base{font-size:var(--text-base,1rem);line-height:var(--tw-leading,var(--text-base--line-height,calc(1.5 / 1)))}.text-lg{font-size:var(--text-lg,1.125rem);line-height:var(--tw-leading,var(--text-lg--line-height,calc(1.75 / 1.125)))}.text-sm{font-size:var(--text-sm,.875rem);line-height:var(--tw-leading,var(--text-sm--line-height,calc(1.25 / .875)))}.text-xl{font-size:var(--text-xl,1.25rem);line-height:var(--tw-leading,var(--text-xl--line-height,calc(1.75 / 1.25)))}.text-xs{font-size:var(--text-xs,.75rem);line-height:var(--tw-leading,var(--text-xs--line-height,calc(1 / .75)))}.text-\[11px\]{font-size:11px}.leading-5{--tw-leading:calc(var(--spacing,.25rem) * 5);line-height:calc(var(--spacing,.25rem) * 5)}.font-medium{--tw-font-weight:var(--font-weight-medium,500);font-weight:var(--font-weight-medium,500)}.font-normal{--tw-font-weight:var(--font-weight-normal,400);font-weight:var(--font-weight-normal,400)}.font-semibold{--tw-font-weight:var(--font-weight-semibold,600);font-weight:var(--font-weight-semibold,600)}.tracking-wide{--tw-tracking:var(--tracking-wide,.025em);letter-spacing:var(--tracking-wide,.025em)}.tracking-wider{--tw-tracking:var(--tracking-wider,.05em);letter-spacing:var(--tracking-wider,.05em)}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.text-alert-error-text{color:var(--color-alert-error-text)}.text-alert-warning-text{color:var(--color-alert-warning-text)}.text-body{color:var(--color-body)}.text-heading{color:var(--color-heading)}.text-muted{color:var(--color-muted)}.text-muted\!{color:var(--color-muted)!important}.text-primary-500{color:var(--color-primary-500)}.text-primary-500\!{color:var(--color-primary-500)!important}.text-status-green{color:var(--color-status-green)}.text-status-red{color:var(--color-status-red)}.text-subtle{color:var(--color-subtle)}.text-white{color:var(--color-white,#fff)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-40{opacity:.4}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function,cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration,var(--default-transition-duration,.15s))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function,cubic-bezier(.4, 0, .2, 1)));transition-duration:var(--tw-duration,var(--default-transition-duration,.15s))}@media (hover:hover){.group-hover\:text-muted:is(:where(.group):hover *){color:var(--color-muted)}.hover\:border-line-strong:hover{border-color:var(--color-line-strong)}.hover\:border-primary-500:hover{border-color:var(--color-primary-500)}.hover\:bg-btn-primary-hover:hover{background-color:var(--color-btn-primary-hover)}.hover\:bg-hover:hover{background-color:var(--color-hover)}.hover\:bg-white\/20:hover{background-color:#fff3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-white\/20:hover{background-color:color-mix(in oklab, var(--color-white,#fff) 20%, transparent)}}.hover\:text-alert-error-text:hover{color:var(--color-alert-error-text)}.hover\:text-body:hover{color:var(--color-body)}.hover\:text-heading:hover{color:var(--color-heading)}.hover\:text-primary-500:hover{color:var(--color-primary-500)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-primary-400:focus{border-color:var(--color-primary-400)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary-400:focus{--tw-ring-color:var(--color-primary-400)}.focus\:outline-hidden:focus{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.focus\:outline-hidden:focus{outline-offset:2px;outline:2px solid #0000}}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}@media not all and (width>=40rem){.max-sm\:hidden{display:none}}@media (width>=40rem){.sm\:w-48{width:calc(var(--spacing,.25rem) * 48)}.sm\:w-56{width:calc(var(--spacing,.25rem) * 56)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (width>=48rem){.md\:h-9{height:calc(var(--spacing,.25rem) * 9)}.md\:px-3{padding-inline:calc(var(--spacing,.25rem) * 3)}}@media (width>=64rem){.lg\:mb-1{margin-bottom:var(--spacing,.25rem)}.lg\:block{display:block}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-end{align-items:flex-end}}@media (width>=80rem){.xl\:grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} /*$vite$:1*/ \ No newline at end of file diff --git a/resources/js/api/billing.ts b/resources/js/api/billing.ts new file mode 100644 index 0000000..d934516 --- /dev/null +++ b/resources/js/api/billing.ts @@ -0,0 +1,210 @@ +import type { AxiosInstance } from 'axios' +import type { Wrapped } from '@/types/api' +import type { + BillingCustomer, + BillingGrouping, + CompanyInvoiceDefaults, + ConfirmItem, + CreatedInvoice, + CurrencyFormat, + InvoicePayload, + InvoiceTemplate, + PreparedInvoice, + UnbilledCustomer, + UnbilledTime, +} from '@/types/billing' + +const BASE = '/api/v1/tasks-projects' + +/** The module endpoints the wizard talks to. */ +export const BILLING_API = { + customers: `${BASE}/billing/customers`, + unbilled: `${BASE}/billing/unbilled`, + prepare: `${BASE}/billing/prepare`, + confirm: `${BASE}/billing/confirm`, +} as const + +/** + * Host endpoints, reached through the same session client. + * + * The invoice itself is written by the host's own endpoint rather than by the + * module, which is what keeps the module out of the invoice tables: same + * session, same permission checks, same validation and same numbering. + */ +export const HOST_BILLING_API = { + bootstrap: '/api/v1/bootstrap', + customers: '/api/v1/customers', + invoices: '/api/v1/invoices', + invoiceTemplates: '/api/v1/invoices/templates', + nextNumber: '/api/v1/next-number', + exchangeRate: (currencyId: number): string => `/api/v1/currencies/${currencyId}/exchange-rate`, +} as const + +/** How many contacts the name lookup asks for. */ +export const CUSTOMER_LOOKUP_LIMIT = 200 + +export interface UnbilledRange { + /** `Y-m-d`, inclusive. */ + from?: string + /** `Y-m-d`, inclusive. */ + to?: string +} + +/** Who has billable time waiting, and how much of it. */ +export async function listUnbilledCustomers( + client: AxiosInstance, + range: UnbilledRange = {}, +): Promise { + const { data } = await client.get>(BILLING_API.customers, { + params: range, + }) + + return data.data ?? [] +} + +/** One customer's unbilled entries, with the four grouped views over them. */ +export async function fetchUnbilledTime( + client: AxiosInstance, + customerId: number, + range: UnbilledRange = {}, +): Promise { + const { data } = await client.get>(BILLING_API.unbilled, { + params: { customer_id: customerId, ...range }, + }) + + return data.data +} + +/** The invoice body for a selection, plus the entries behind each line. */ +export async function prepareInvoice( + client: AxiosInstance, + entryIds: number[], + grouping: BillingGrouping, +): Promise { + const { data } = await client.post>(BILLING_API.prepare, { + entry_ids: entryIds, + grouping, + }) + + return data.data +} + +/** + * Stamp the entries with the ids the host handed back. + * + * Idempotent, so a wizard that created the invoice and then lost the stamp can + * offer the same call again rather than a second invoice. + */ +export async function confirmInvoice( + client: AxiosInstance, + invoiceId: number, + items: ConfirmItem[], +): Promise { + const { data } = await client.post<{ stamped?: number }>(BILLING_API.confirm, { + invoice_id: invoiceId, + items, + }) + + return data?.stamped ?? 0 +} + +/** The company's contacts, for turning a customer id into a name. */ +export async function listBillingCustomers( + client: AxiosInstance, + limit = CUSTOMER_LOOKUP_LIMIT, +): Promise { + const { data } = await client.get>(HOST_BILLING_API.customers, { + params: { limit }, + }) + + return data.data ?? [] +} + +/** Create the draft invoice with the session's own client. */ +export async function createInvoice( + client: AxiosInstance, + payload: InvoicePayload, +): Promise { + const { data } = await client.post>(HOST_BILLING_API.invoices, payload) + + return data.data +} + +export async function listInvoiceTemplates(client: AxiosInstance): Promise { + const { data } = await client.get<{ invoiceTemplates?: InvoiceTemplate[] }>( + HOST_BILLING_API.invoiceTemplates, + ) + + return data?.invoiceTemplates ?? [] +} + +/** + * The number the next invoice would carry. + * + * The customer goes along as `userId`, which is what the host's serial + * numbering calls it, so a per-customer number format resolves the same way it + * does on the host's own form. + */ +export async function fetchNextInvoiceNumber( + client: AxiosInstance, + customerId?: number, +): Promise { + const params: Record = { key: 'invoice' } + + if (customerId !== undefined) { + params.userId = customerId + } + + const { data } = await client.get<{ success?: boolean; nextNumber?: string }>( + HOST_BILLING_API.nextNumber, + { params }, + ) + + return data?.success && typeof data.nextNumber === 'string' ? data.nextNumber : null +} + +/** + * The rate from a customer's currency into the company's own. + * + * The endpoint answers a bare number, a one-element array or an error object + * depending on whether a live provider, a logged rate or nothing at all + * supplied it, so all three are read here and anything else becomes null. + */ +export async function fetchExchangeRate( + client: AxiosInstance, + currencyId: number, +): Promise { + const { data } = await client.get<{ exchangeRate?: unknown }>( + HOST_BILLING_API.exchangeRate(currencyId), + ) + const rate = Array.isArray(data?.exchangeRate) ? data.exchangeRate[0] : data?.exchangeRate + const value = Number(rate) + + return Number.isFinite(value) && value > 0 ? value : null +} + +/** The company's invoice defaults, read from the host bootstrap payload. */ +export async function fetchCompanyInvoiceDefaults( + client: AxiosInstance, +): Promise { + const { data } = await client.get<{ + current_company_settings?: Record + current_company_currency?: CurrencyFormat | null + current_user_settings?: Record + }>(HOST_BILLING_API.bootstrap) + + const settings = data?.current_company_settings ?? {} + const userSettings = data?.current_user_settings ?? {} + const days = Number(settings.invoice_due_date_days) + const template = userSettings.default_invoice_template + + return { + currency: data?.current_company_currency ?? null, + dueDateDays: Number.isFinite(days) && days >= 0 ? days : 0, + setDueDateAutomatically: settings.invoice_set_due_date_automatically === 'YES', + // Anything but an explicit NO leaves the host numbering the invoice, which + // is what an older company with no stored value expects. + autoGenerateNumber: settings.invoice_auto_generate !== 'NO', + defaultTemplate: typeof template === 'string' && template !== '' ? template : null, + } +} diff --git a/resources/js/init.ts b/resources/js/init.ts index 360f625..701136a 100644 --- a/resources/js/init.ts +++ b/resources/js/init.ts @@ -6,6 +6,7 @@ import { messages } from './messages' import ProjectsIndexPage from './pages/ProjectsIndexPage.vue' import { registerTimeTracking } from './registrations/time' import { registerBoardPages } from './registrations/board' +import { registerBillingPages } from './registrations/billing' import { registerReportPages } from './registrations/reports' const MODULE = 'tasks-projects' @@ -26,6 +27,7 @@ window.InvoiceShelf.booting((_app, _router, extensions) => { registerTimeTracking(extensions) registerBoardPages(extensions) + registerBillingPages(extensions) registerReportPages(extensions) }) diff --git a/resources/js/messages/billing.ts b/resources/js/messages/billing.ts new file mode 100644 index 0000000..b84d26b --- /dev/null +++ b/resources/js/messages/billing.ts @@ -0,0 +1,109 @@ +/** + * Every string the billing wizard renders. + * + * Kept beside the slice that owns it rather than in `messages.ts`, so two + * slices of the module never edit the same catalogue. The host merges each + * bundle recursively, so these land under the same `tasks_projects` namespace + * as the rest. + */ +export const billingMessages = { + en: { + tasks_projects: { + billing: { + title: 'Invoice time', + subtitle: 'Turn unbilled hours into a draft invoice.', + invoice_time: 'Invoice time', + unbilled: 'Unbilled', + view_unbilled: 'Invoice this time', + steps: { + customer: 'Customer', + entries: 'Entries', + preview: 'Preview', + create: 'Create', + }, + back: 'Back', + next: 'Continue', + start_over: 'Start over', + customer: { + title: 'Who are you invoicing?', + description: 'Customers with billable time that has not reached an invoice yet.', + entries: '{count} entries', + empty_title: 'Nothing to invoice', + empty_description: + 'Billable time appears here once it has been logged against a task that belongs to a customer.', + load_failed: 'Unable to load the customers with unbilled time.', + names_failed: 'Unable to load the customer names; ids are shown instead.', + unnamed: 'Customer #{id}', + from: 'From', + to: 'To', + clear_range: 'Clear dates', + }, + entries: { + title: 'Which time goes on the invoice?', + grouping: 'Group lines by', + group_by: { + task: 'Task', + project: 'Project', + member: 'Member', + summary: 'One summary line', + }, + select_all: 'Select all', + selected: '{count} of {total} entries selected', + selected_total: 'Selected: {hours}', + no_description: 'No description', + columns: { + date: 'Date', + task: 'Task', + project: 'Project', + member: 'Member', + duration: 'Duration', + amount: 'Amount', + }, + empty_title: 'No unbilled time', + empty_description: 'This customer has nothing waiting to be invoiced in this range.', + load_failed: 'Unable to load the unbilled time.', + none_selected: 'Select at least one entry.', + }, + preview: { + title: 'Check the invoice', + lines: 'Invoice lines', + columns: { + description: 'Description', + quantity: 'Hours', + price: 'Rate', + total: 'Amount', + }, + sub_total: 'Subtotal', + total: 'Total', + invoice_date: 'Invoice date', + due_date: 'Due date', + invoice_number: 'Invoice number', + invoice_number_auto: 'Generated by the company number format.', + template: 'Template', + exchange_rate: 'Exchange rate', + exchange_rate_help: '1 {currency} in the company currency.', + prepare_failed: 'Unable to prepare the invoice.', + templates_failed: 'Unable to load the invoice templates.', + number_failed: 'Unable to read the next invoice number. Type one in.', + rate_failed: 'Unable to read the exchange rate. Type one in.', + create: 'Create invoice', + invalid: 'The invoice was refused. Fix the fields below and try again.', + }, + create: { + creating: 'Creating the invoice', + stamping: 'Marking the time as invoiced', + created_title: 'Invoice {number} created', + created_description: '{count} entries were marked as invoiced.', + view_invoice: 'Open the invoice', + invoice_more: 'Invoice more time', + failed: 'Unable to create the invoice.', + stamp_failed_title: 'The invoice was created, but the time is not marked yet', + stamp_failed_description: + 'Invoice {number} exists. The time entries still count as unbilled until they are stamped, which is safe to run again.', + retry_stamp: 'Retry stamping', + stamped: 'The time entries were marked as invoiced.', + }, + }, + }, + }, +} diff --git a/resources/js/pages/BillingPage.vue b/resources/js/pages/BillingPage.vue new file mode 100644 index 0000000..216bb51 --- /dev/null +++ b/resources/js/pages/BillingPage.vue @@ -0,0 +1,1163 @@ + + + diff --git a/resources/js/pages/TimePage.vue b/resources/js/pages/TimePage.vue index f2415d0..be018fe 100644 --- a/resources/js/pages/TimePage.vue +++ b/resources/js/pages/TimePage.vue @@ -87,7 +87,7 @@ async function loadMembers(): Promise { async function loadProjects(): Promise { try { - const response = await listProjects(props.client, { limit: 100 }) + const response = await listProjects(props.client, { limit: 100, sort_by: 'name' }) projects.value = response.data ?? [] } catch { @@ -161,12 +161,21 @@ function tabClass(value: TimeTab): string {
+ + + + {{ t('tasks_projects.billing.invoice_time') }} + + +