aboutsummaryrefslogtreecommitdiffstats
path: root/old-apps/projects/src/app
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-10-05 14:45:21 +0200
committerivarlovlie <git@ivarlovlie.no>2022-10-05 14:45:21 +0200
commitb7e39b59fd0fc7b5610ebff29035bf622079e0d8 (patch)
tree64be84ebbdac9f7ceced983390c53b10d575af5c /old-apps/projects/src/app
parent2001c035fbb417ab0a3d42cfb04d17420bde4086 (diff)
downloadgreatoffice-b7e39b59fd0fc7b5610ebff29035bf622079e0d8.tar.xz
greatoffice-b7e39b59fd0fc7b5610ebff29035bf622079e0d8.zip
refactor: Change file structure
Diffstat (limited to 'old-apps/projects/src/app')
-rw-r--r--old-apps/projects/src/app/index.d.ts48
-rw-r--r--old-apps/projects/src/app/index.html63
-rw-r--r--old-apps/projects/src/app/index.scss40
-rw-r--r--old-apps/projects/src/app/index.svelte96
-rw-r--r--old-apps/projects/src/app/index.ts16
-rw-r--r--old-apps/projects/src/app/lib/i18n/en/index.ts126
-rw-r--r--old-apps/projects/src/app/lib/i18n/formatters.ts11
-rw-r--r--old-apps/projects/src/app/lib/i18n/i18n-svelte.ts12
-rw-r--r--old-apps/projects/src/app/lib/i18n/i18n-types.ts822
-rw-r--r--old-apps/projects/src/app/lib/i18n/i18n-util.async.ts27
-rw-r--r--old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts26
-rw-r--r--old-apps/projects/src/app/lib/i18n/i18n-util.ts31
-rw-r--r--old-apps/projects/src/app/lib/i18n/nb/index.ts126
-rw-r--r--old-apps/projects/src/app/lib/services/user-service.ts14
-rw-r--r--old-apps/projects/src/app/lib/stores/categories.ts44
-rw-r--r--old-apps/projects/src/app/lib/stores/entries.ts74
-rw-r--r--old-apps/projects/src/app/lib/stores/labels.ts44
-rw-r--r--old-apps/projects/src/app/pages/_layout.svelte66
-rw-r--r--old-apps/projects/src/app/pages/data.svelte396
-rw-r--r--old-apps/projects/src/app/pages/home.svelte178
-rw-r--r--old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css179
-rw-r--r--old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css213
-rw-r--r--old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html211
-rw-r--r--old-apps/projects/src/app/pages/nav/index.ts6
-rw-r--r--old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js296
-rw-r--r--old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js215
-rw-r--r--old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js73
-rw-r--r--old-apps/projects/src/app/pages/nav/nav-item.svelte18
-rw-r--r--old-apps/projects/src/app/pages/nav/nav-wrapper.svelte20
-rw-r--r--old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss147
-rw-r--r--old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss237
-rw-r--r--old-apps/projects/src/app/pages/nav/side-navigation-v4.zipbin13503 -> 0 bytes
-rw-r--r--old-apps/projects/src/app/pages/not-found.svelte25
-rw-r--r--old-apps/projects/src/app/pages/settings.svelte12
-rw-r--r--old-apps/projects/src/app/pages/ui-workbench.svelte7
-rw-r--r--old-apps/projects/src/app/pages/views/category-form/index.svelte144
-rw-r--r--old-apps/projects/src/app/pages/views/data-table-paginator.svelte101
-rw-r--r--old-apps/projects/src/app/pages/views/entry-form/index.svelte199
-rw-r--r--old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte76
-rw-r--r--old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte167
-rw-r--r--old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte66
-rw-r--r--old-apps/projects/src/app/pages/views/profile-modal.svelte156
-rw-r--r--old-apps/projects/src/app/pages/views/settings-categories-tile.svelte126
-rw-r--r--old-apps/projects/src/app/pages/views/settings-labels-tile.svelte111
44 files changed, 0 insertions, 5065 deletions
diff --git a/old-apps/projects/src/app/index.d.ts b/old-apps/projects/src/app/index.d.ts
deleted file mode 100644
index c044583..0000000
--- a/old-apps/projects/src/app/index.d.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Use this file to declare any custom file extensions for importing */
-/* Use this folder to also add/extend a package d.ts file, if needed. */
-
-/* CSS MODULES */
-declare module "*.module.css" {
- const classes: { [key: string]: string };
- export default classes;
-}
-declare module "*.module.scss" {
- const classes: { [key: string]: string };
- export default classes;
-}
-
-/* CSS */
-declare module "*.css";
-declare module "*.scss";
-
-/* IMAGES */
-declare module "*.svg" {
- const ref: string;
- export default ref;
-}
-declare module "*.bmp" {
- const ref: string;
- export default ref;
-}
-declare module "*.gif" {
- const ref: string;
- export default ref;
-}
-declare module "*.jpg" {
- const ref: string;
- export default ref;
-}
-declare module "*.jpeg" {
- const ref: string;
- export default ref;
-}
-declare module "*.png" {
- const ref: string;
- export default ref;
-}
-
-/* CUSTOM: ADD YOUR OWN HERE */
-declare module "*.svelte" {
- const value: any;
- export default value;
-}
diff --git a/old-apps/projects/src/app/index.html b/old-apps/projects/src/app/index.html
deleted file mode 100644
index 7e0b0e1..0000000
--- a/old-apps/projects/src/app/index.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!doctype html>
-<html lang="en">
-
-<head>
- <meta charset="UTF-8">
- <meta name="viewport"
- content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
- <link rel="apple-touch-icon"
- sizes="180x180"
- href="../_assets/pwa/apple-touch-icon.png">
- <link rel="icon"
- type="image/png"
- sizes="32x32"
- href="../_assets/pwa/favicon-32x32.png">
- <link rel="icon"
- type="image/png"
- sizes="16x16"
- href="../_assets/pwa/favicon-16x16.png">
- <link rel="manifest"
- href="../_assets/pwa/manifest.json">
- <link rel="mask-icon"
- href="../_assets/pwa/safari-pinned-tab.svg"
- color="#5bbad5">
- <meta name="msapplication-TileColor"
- content="#da532c">
- <link rel="icon"
- href="../_assets/pwa/favicon.svg">
- <script>
- const currentTheme = localStorage.getItem("theme");
- if (currentTheme === "light") {
- document.querySelector("html").dataset.theme = "light";
- } else {
- document.querySelector("html").dataset.theme = "dark";
- }
- </script>
- <link rel="stylesheet"
- href="../_assets/pre.css">
- <title>Time Tracker</title>
-</head>
-
-<body>
-
-<noscript>
- This page is built with javascript. Allow it and try again.
-</noscript>
-
-<div class="fill-loader fill-loader--v4"
- id="loader"
- role="alert">
- <p class="fill-loader__label">Loading Time Tracker...</p>
- <div aria-hidden="true">
- <div class="fill-loader__base"></div>
- <div class="fill-loader__fill"></div>
- </div>
-</div>
-
-<div id="root"></div>
-
-<script type="module"
- src="./index.ts"></script>
-</body>
-
-</html>
diff --git a/old-apps/projects/src/app/index.scss b/old-apps/projects/src/app/index.scss
deleted file mode 100644
index f83b1a1..0000000
--- a/old-apps/projects/src/app/index.scss
+++ /dev/null
@@ -1,40 +0,0 @@
-@use '../../web-shared/src/styles/base'as * with ($breakpoints: ('xs': "768px",
- 'sm': "768px",
- 'md': "1200px",
- 'lg': "1200px",
- 'xl': "1600px",
- ),
- $grid-columns: 12);
-
-@use '../../web-shared/src/styles/custom-style/colors';
-@use '../../web-shared/src/styles/custom-style/spacing';
-@use '../../web-shared/src/styles/custom-style/shared-styles';
-@use '../../web-shared/src/styles/custom-style/typography';
-@use '../../web-shared/src/styles/custom-style/icons';
-@use '../../web-shared/src/styles/custom-style/buttons';
-@use '../../web-shared/src/styles/custom-style/forms';
-@use '../../web-shared/src/styles/custom-style/util';
-
-@use '../../web-shared/src/styles/components/radios-checkboxes';
-@use '../../web-shared/src/styles/components/circle-loader';
-@use '../../web-shared/src/styles/components/list';
-@use '../../web-shared/src/styles/components/form-validator';
-@use '../../web-shared/src/styles/components/btn-states';
-@use '../../web-shared/src/styles/components/alert';
-@use '../../web-shared/src/styles/components/details';
-@use '../../web-shared/src/styles/components/tabbed-navigation';
-@use '../../web-shared/src/styles/components/dropdown';
-@use '../../web-shared/src/styles/components/modal';
-@use '../../web-shared/src/styles/components/chip';
-@use '../../web-shared/src/styles/components/autocomplete';
-@use '../../web-shared/src/styles/components/select-autocomplete';
-@use '../../web-shared/src/styles/components/interactive-table';
-@use '../../web-shared/src/styles/components/pagination';
-@use '../../web-shared/src/styles/components/custom-select';
-@use '../../web-shared/src/styles/components/pre-header';
-@use '../../web-shared/src/styles/components/table';
-@use '../../web-shared/src/styles/components/custom-checkbox';
-@use '../../web-shared/src/styles/components/menu';
-@use '../../web-shared/src/styles/components/user-menu';
-@use '../../web-shared/src/styles/components/light-dark-switch';
-@use '../../web-shared/src/styles/components/side-navigation-v4';
diff --git a/old-apps/projects/src/app/index.svelte b/old-apps/projects/src/app/index.svelte
deleted file mode 100644
index c121a32..0000000
--- a/old-apps/projects/src/app/index.svelte
+++ /dev/null
@@ -1,96 +0,0 @@
-<svelte:options immutable={true}/>
-<svelte:window bind:online={online}/>
-
-<script lang="ts">
- import { Locales } from "$app/lib/i18n/i18n-types";
- import { logout_user } from "$app/lib/services/user-service";
- import { currentLocale, preffered_or_default } from "$shared/lib/locale";
- import { CookieNames } from "$shared/lib/configuration";
- import { get_cookie } from "$shared/lib/helpers";
- import { Temporal } from "@js-temporal/polyfill";
- import { onMount } from "svelte";
- import Router from "svelte-spa-router";
- import { wrap } from "svelte-spa-router/wrap";
- import { QueryClient, QueryClientProvider } from "@sveltestack/svelte-query";
- import { is_active } from "$shared/lib/session";
- import UiWorkbench from "$app/pages/ui-workbench.svelte";
- import NotFound from "$app/pages/not-found.svelte";
- import Home from "$app/pages/home.svelte";
- import Settings from "$app/pages/settings.svelte";
- import Data from "$app/pages/data.svelte";
- import PreHeader from "$shared/components/pre-header.svelte";
- import { setLocale } from "$app/lib/i18n/i18n-svelte";
- import { loadLocaleAsync } from "$app/lib/i18n/i18n-util.async";
- import { i18nObject } from "$app/lib/i18n/i18n-util";
-
- let online = true;
- let notOnlineText;
- let LL;
-
- console.log("Projects Startup Report", {
- prefferedLocale: navigator.language,
- timeZone: Temporal.Now.timeZone().id,
- themeCookie: {name: CookieNames.theme, value: get_cookie(CookieNames.theme)},
- localeCookie: {name: CookieNames.locale, value: get_cookie(CookieNames.locale)},
- prefersColorScheme: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
- });
-
- currentLocale.subscribe(async locale => {
- if (locale === "preffered") locale = preffered_or_default();
- await loadLocaleAsync(locale as Locales);
- LL = i18nObject(locale as Locales);
- setLocale(locale as Locales);
- });
-
- onMount(async () => {
- await loadLocaleAsync($currentLocale);
- LL = i18nObject($currentLocale);
- setLocale($currentLocale);
- notOnlineText = LL.messages.noInternet();
- });
-
- async function user_is_logged_in() {
- if (!await is_active()) {
- await logout_user("expired");
- }
- return true;
- }
-
- const queryClient = new QueryClient();
-
- const routes = {
- "/home": wrap({
- component: Home,
- conditions: [user_is_logged_in],
- }),
- "/": wrap({
- component: Home,
- conditions: [user_is_logged_in],
- }),
- "/settings": wrap({
- component: Settings,
- conditions: [user_is_logged_in],
- }),
- "/data": wrap({
- component: Data,
- conditions: [user_is_logged_in],
- }),
- "/ui-workbench": UiWorkbench,
- "*": NotFound,
- };
-</script>
-
-<PreHeader show="{!online}">{notOnlineText}</PreHeader>
-
-<QueryClientProvider client={queryClient}>
- <Router
- {routes}
- restoreScrollState={true}
- on:routeLoading={() => {
- document.getElementById("loader").style.display = "inline-block";
- }}
- on:routeLoaded={() => {
- document.getElementById("loader").style.display = "none";
- }}
- />
-</QueryClientProvider>
diff --git a/old-apps/projects/src/app/index.ts b/old-apps/projects/src/app/index.ts
deleted file mode 100644
index febb583..0000000
--- a/old-apps/projects/src/app/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// @ts-ignore
-import App from "./index.svelte";
-import "./index.scss";
-import {is_debug, is_development} from "$shared/lib/configuration";
-import {noop} from "$shared/lib/helpers";
-
-if (is_development() || is_debug()) {
- console.log("%c Debug", "background-color:yellow;color:black;font-size:18px;");
-} else {
- console.log("%c Production; Suppressing logs", "background-color:yellow;color:black;font-size:18px;");
- console.log = noop;
-}
-
-export default new App({
- target: document.getElementById("root"),
-});
diff --git a/old-apps/projects/src/app/lib/i18n/en/index.ts b/old-apps/projects/src/app/lib/i18n/en/index.ts
deleted file mode 100644
index a85af7b..0000000
--- a/old-apps/projects/src/app/lib/i18n/en/index.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import type {BaseTranslation} from "../i18n-types";
-
-const en: BaseTranslation = {
- nav: {
- home: "Home",
- data: "Data",
- settings: "Settings",
- usermenu: {
- logout: "Log out",
- logoutTitle: "Log out of your profile",
- profile: "Profile",
- profileTitle: "Administrate your profile",
- toggleTitle: "Toggle user menu",
- }
- },
- views: {
- dataTablePaginator: {
- goToPrevPage: "Go to previous page",
- goToNextPage: "Go to next page",
- of: "of",
- },
- categoryForm: {
- name: "Name",
- color: "Color",
- defaultLabels: "Default labels",
- labelsPlaceholder: "Search or create"
- },
- settingsCategoriesTile: {
- deleteAllConfirm: "Are you sure you want to delete this category?\nThis will delete all relating entries!",
- active: "Active",
- archived: "Archived",
- name: "Name",
- color: "Color",
- editEntry: "Edit entry",
- deleteEntry: "Delete entry",
- noCategories: "No categories",
- categories: "Categories"
- },
- settingsLabelsTile: {
- deleteAllConfirm: "Are you sure you want to delete this label?\nIt will be removed from all related entries!",
- active: "Active",
- archived: "Archived",
- name: "Name",
- color: "Color",
- editEntry: "Edit label",
- deleteEntry: "Delete label",
- noLabels: "No labels",
- labels: "Labels"
- },
- entryForm: {
- entryUpdateError: "An error occured while updating the entry, try again soon.",
- entryCreateError: "An error occured while creating the entry, try again soon.",
- errDescriptionReq: "Description is required",
- reset: "Reset",
- description: "Description",
- save: "Save",
- create: "Create",
- category: {
- category: "Category",
- placeholder: "Search or create",
- noResults: "No categories available (Create a new one by searching for it)",
- errisRequired: "Category is required",
- _logReset: "Reset category section"
- },
- labels: {
- placeholder: "Search or create",
- noResults: "No labels available (Create a new one by searching for it)",
- labels: "Labels",
- _logReset: "Reset labels section"
- },
- dateTime: {
- errDateIsRequired: "Date is required",
- errFromIsRequired: "From is required",
- errFromAfterTo: "From can not be after To",
- errFromEqTo: "From and To can not be equal",
- errToIsRequired: "To is required",
- errToBeforeFrom: "To can not be before From",
- from: "From",
- to: "To",
- date: "Date",
- _logReset: "Reset date time section"
- }
- }
- },
- data: {
- durationSummary: "Showing {entryCountString:string}, totalling in {totalHourMin:string}",
- hourSingleChar: "h",
- minSingleChar: "m",
- entry: "entry",
- entries: "entries",
- confirmDeleteEntry: "Are you sure you want to delete this entry?",
- editEntry: "Edit entry",
- date: "Date",
- from: "From",
- duration: "Duration",
- category: "Category",
- description: "Description",
- loading: "Loading",
- noEntries: "No entries",
- to: "to",
- use: "Use",
- },
- home: {
- confirmDeleteEntry: "Are you sure you want to delete this entry?",
- newEntry: "New entry",
- editEntry: "Edit entry",
- deleteEntry: "Delete entry",
- loggedTimeToday: "Logged time today",
- loggedTimeTodayString: "{hours}h{minutes}m",
- currentTime: "Current time",
- loading: "Loading",
- stopwatch: "Stopwatch",
- todayEntries: "Today's entries",
- noEntriesToday: "No entries today",
- refreshTodayEntries: "Refresh today's entries",
- category: "Category",
- timespan: "Timespan",
- },
- messages: {
- pageNotFound: "Page not found",
- goToFrontpage: "Go to frontpage",
- noInternet: "It seems like your device does not have a internet connection, please check your connection."
- }
-};
-
-export default en;
diff --git a/old-apps/projects/src/app/lib/i18n/formatters.ts b/old-apps/projects/src/app/lib/i18n/formatters.ts
deleted file mode 100644
index 78734f9..0000000
--- a/old-apps/projects/src/app/lib/i18n/formatters.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { FormattersInitializer } from 'typesafe-i18n'
-import type { Locales, Formatters } from './i18n-types'
-
-export const initFormatters: FormattersInitializer<Locales, Formatters> = (locale: Locales) => {
-
- const formatters: Formatters = {
- // add your formatter functions here
- }
-
- return formatters
-}
diff --git a/old-apps/projects/src/app/lib/i18n/i18n-svelte.ts b/old-apps/projects/src/app/lib/i18n/i18n-svelte.ts
deleted file mode 100644
index 6cdffb3..0000000
--- a/old-apps/projects/src/app/lib/i18n/i18n-svelte.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
-/* eslint-disable */
-
-import { initI18nSvelte } from 'typesafe-i18n/svelte'
-import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types'
-import { loadedFormatters, loadedLocales } from './i18n-util'
-
-const { locale, LL, setLocale } = initI18nSvelte<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters)
-
-export { locale, LL, setLocale }
-
-export default LL
diff --git a/old-apps/projects/src/app/lib/i18n/i18n-types.ts b/old-apps/projects/src/app/lib/i18n/i18n-types.ts
deleted file mode 100644
index acba223..0000000
--- a/old-apps/projects/src/app/lib/i18n/i18n-types.ts
+++ /dev/null
@@ -1,822 +0,0 @@
-// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
-/* eslint-disable */
-import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n'
-
-export type BaseTranslation = BaseTranslationType
-export type BaseLocale = 'en'
-
-export type Locales =
- | 'en'
- | 'nb'
-
-export type Translation = RootTranslation
-
-export type Translations = RootTranslation
-
-type RootTranslation = {
- nav: {
- /**
- * Home
- */
- home: string
- /**
- * Data
- */
- data: string
- /**
- * Settings
- */
- settings: string
- usermenu: {
- /**
- * Log out
- */
- logout: string
- /**
- * Log out of your profile
- */
- logoutTitle: string
- /**
- * Profile
- */
- profile: string
- /**
- * Administrate your profile
- */
- profileTitle: string
- /**
- * Toggle user menu
- */
- toggleTitle: string
- }
- }
- views: {
- dataTablePaginator: {
- /**
- * Go to previous page
- */
- goToPrevPage: string
- /**
- * Go to next page
- */
- goToNextPage: string
- /**
- * of
- */
- of: string
- }
- categoryForm: {
- /**
- * Name
- */
- name: string
- /**
- * Color
- */
- color: string
- /**
- * Default labels
- */
- defaultLabels: string
- /**
- * Search or create
- */
- labelsPlaceholder: string
- }
- settingsCategoriesTile: {
- /**
- * Are you sure you want to delete this category?
- This will delete all relating entries!
- */
- deleteAllConfirm: string
- /**
- * Active
- */
- active: string
- /**
- * Archived
- */
- archived: string
- /**
- * Name
- */
- name: string
- /**
- * Color
- */
- color: string
- /**
- * Edit entry
- */
- editEntry: string
- /**
- * Delete entry
- */
- deleteEntry: string
- /**
- * No categories
- */
- noCategories: string
- /**
- * Categories
- */
- categories: string
- }
- settingsLabelsTile: {
- /**
- * Are you sure you want to delete this label?
- It will be removed from all related entries!
- */
- deleteAllConfirm: string
- /**
- * Active
- */
- active: string
- /**
- * Archived
- */
- archived: string
- /**
- * Name
- */
- name: string
- /**
- * Color
- */
- color: string
- /**
- * Edit label
- */
- editEntry: string
- /**
- * Delete label
- */
- deleteEntry: string
- /**
- * No labels
- */
- noLabels: string
- /**
- * Labels
- */
- labels: string
- }
- entryForm: {
- /**
- * An error occured while updating the entry, try again soon.
- */
- entryUpdateError: string
- /**
- * An error occured while creating the entry, try again soon.
- */
- entryCreateError: string
- /**
- * Description is required
- */
- errDescriptionReq: string
- /**
- * Reset
- */
- reset: string
- /**
- * Description
- */
- description: string
- /**
- * Save
- */
- save: string
- /**
- * Create
- */
- create: string
- category: {
- /**
- * Category
- */
- category: string
- /**
- * Search or create
- */
- placeholder: string
- /**
- * No categories available (Create a new one by searching for it)
- */
- noResults: string
- /**
- * Category is required
- */
- errisRequired: string
- /**
- * Reset category section
- */
- _logReset: string
- }
- labels: {
- /**
- * Search or create
- */
- placeholder: string
- /**
- * No labels available (Create a new one by searching for it)
- */
- noResults: string
- /**
- * Labels
- */
- labels: string
- /**
- * Reset labels section
- */
- _logReset: string
- }
- dateTime: {
- /**
- * Date is required
- */
- errDateIsRequired: string
- /**
- * From is required
- */
- errFromIsRequired: string
- /**
- * From can not be after To
- */
- errFromAfterTo: string
- /**
- * From and To can not be equal
- */
- errFromEqTo: string
- /**
- * To is required
- */
- errToIsRequired: string
- /**
- * To can not be before From
- */
- errToBeforeFrom: string
- /**
- * From
- */
- from: string
- /**
- * To
- */
- to: string
- /**
- * Date
- */
- date: string
- /**
- * Reset date time section
- */
- _logReset: string
- }
- }
- }
- data: {
- /**
- * Showing {entryCountString}, totalling in {totalHourMin}
- * @param {string} entryCountString
- * @param {string} totalHourMin
- */
- durationSummary: RequiredParams<'entryCountString' | 'totalHourMin'>
- /**
- * h
- */
- hourSingleChar: string
- /**
- * m
- */
- minSingleChar: string
- /**
- * entry
- */
- entry: string
- /**
- * entries
- */
- entries: string
- /**
- * Are you sure you want to delete this entry?
- */
- confirmDeleteEntry: string
- /**
- * Edit entry
- */
- editEntry: string
- /**
- * Date
- */
- date: string
- /**
- * From
- */
- from: string
- /**
- * Duration
- */
- duration: string
- /**
- * Category
- */
- category: string
- /**
- * Description
- */
- description: string
- /**
- * Loading
- */
- loading: string
- /**
- * No entries
- */
- noEntries: string
- /**
- * to
- */
- to: string
- /**
- * Use
- */
- use: string
- }
- home: {
- /**
- * Are you sure you want to delete this entry?
- */
- confirmDeleteEntry: string
- /**
- * New entry
- */
- newEntry: string
- /**
- * Edit entry
- */
- editEntry: string
- /**
- * Delete entry
- */
- deleteEntry: string
- /**
- * Logged time today
- */
- loggedTimeToday: string
- /**
- * {hours}h{minutes}m
- * @param {unknown} hours
- * @param {unknown} minutes
- */
- loggedTimeTodayString: RequiredParams<'hours' | 'minutes'>
- /**
- * Current time
- */
- currentTime: string
- /**
- * Loading
- */
- loading: string
- /**
- * Stopwatch
- */
- stopwatch: string
- /**
- * Today's entries
- */
- todayEntries: string
- /**
- * No entries today
- */
- noEntriesToday: string
- /**
- * Refresh today's entries
- */
- refreshTodayEntries: string
- /**
- * Category
- */
- category: string
- /**
- * Timespan
- */
- timespan: string
- }
- messages: {
- /**
- * Page not found
- */
- pageNotFound: string
- /**
- * Go to frontpage
- */
- goToFrontpage: string
- /**
- * It seems like your device does not have a internet connection, please check your connection.
- */
- noInternet: string
- }
-}
-
-export type TranslationFunctions = {
- nav: {
- /**
- * Home
- */
- home: () => LocalizedString
- /**
- * Data
- */
- data: () => LocalizedString
- /**
- * Settings
- */
- settings: () => LocalizedString
- usermenu: {
- /**
- * Log out
- */
- logout: () => LocalizedString
- /**
- * Log out of your profile
- */
- logoutTitle: () => LocalizedString
- /**
- * Profile
- */
- profile: () => LocalizedString
- /**
- * Administrate your profile
- */
- profileTitle: () => LocalizedString
- /**
- * Toggle user menu
- */
- toggleTitle: () => LocalizedString
- }
- }
- views: {
- dataTablePaginator: {
- /**
- * Go to previous page
- */
- goToPrevPage: () => LocalizedString
- /**
- * Go to next page
- */
- goToNextPage: () => LocalizedString
- /**
- * of
- */
- of: () => LocalizedString
- }
- categoryForm: {
- /**
- * Name
- */
- name: () => LocalizedString
- /**
- * Color
- */
- color: () => LocalizedString
- /**
- * Default labels
- */
- defaultLabels: () => LocalizedString
- /**
- * Search or create
- */
- labelsPlaceholder: () => LocalizedString
- }
- settingsCategoriesTile: {
- /**
- * Are you sure you want to delete this category?
- This will delete all relating entries!
- */
- deleteAllConfirm: () => LocalizedString
- /**
- * Active
- */
- active: () => LocalizedString
- /**
- * Archived
- */
- archived: () => LocalizedString
- /**
- * Name
- */
- name: () => LocalizedString
- /**
- * Color
- */
- color: () => LocalizedString
- /**
- * Edit entry
- */
- editEntry: () => LocalizedString
- /**
- * Delete entry
- */
- deleteEntry: () => LocalizedString
- /**
- * No categories
- */
- noCategories: () => LocalizedString
- /**
- * Categories
- */
- categories: () => LocalizedString
- }
- settingsLabelsTile: {
- /**
- * Are you sure you want to delete this label?
- It will be removed from all related entries!
- */
- deleteAllConfirm: () => LocalizedString
- /**
- * Active
- */
- active: () => LocalizedString
- /**
- * Archived
- */
- archived: () => LocalizedString
- /**
- * Name
- */
- name: () => LocalizedString
- /**
- * Color
- */
- color: () => LocalizedString
- /**
- * Edit label
- */
- editEntry: () => LocalizedString
- /**
- * Delete label
- */
- deleteEntry: () => LocalizedString
- /**
- * No labels
- */
- noLabels: () => LocalizedString
- /**
- * Labels
- */
- labels: () => LocalizedString
- }
- entryForm: {
- /**
- * An error occured while updating the entry, try again soon.
- */
- entryUpdateError: () => LocalizedString
- /**
- * An error occured while creating the entry, try again soon.
- */
- entryCreateError: () => LocalizedString
- /**
- * Description is required
- */
- errDescriptionReq: () => LocalizedString
- /**
- * Reset
- */
- reset: () => LocalizedString
- /**
- * Description
- */
- description: () => LocalizedString
- /**
- * Save
- */
- save: () => LocalizedString
- /**
- * Create
- */
- create: () => LocalizedString
- category: {
- /**
- * Category
- */
- category: () => LocalizedString
- /**
- * Search or create
- */
- placeholder: () => LocalizedString
- /**
- * No categories available (Create a new one by searching for it)
- */
- noResults: () => LocalizedString
- /**
- * Category is required
- */
- errisRequired: () => LocalizedString
- /**
- * Reset category section
- */
- _logReset: () => LocalizedString
- }
- labels: {
- /**
- * Search or create
- */
- placeholder: () => LocalizedString
- /**
- * No labels available (Create a new one by searching for it)
- */
- noResults: () => LocalizedString
- /**
- * Labels
- */
- labels: () => LocalizedString
- /**
- * Reset labels section
- */
- _logReset: () => LocalizedString
- }
- dateTime: {
- /**
- * Date is required
- */
- errDateIsRequired: () => LocalizedString
- /**
- * From is required
- */
- errFromIsRequired: () => LocalizedString
- /**
- * From can not be after To
- */
- errFromAfterTo: () => LocalizedString
- /**
- * From and To can not be equal
- */
- errFromEqTo: () => LocalizedString
- /**
- * To is required
- */
- errToIsRequired: () => LocalizedString
- /**
- * To can not be before From
- */
- errToBeforeFrom: () => LocalizedString
- /**
- * From
- */
- from: () => LocalizedString
- /**
- * To
- */
- to: () => LocalizedString
- /**
- * Date
- */
- date: () => LocalizedString
- /**
- * Reset date time section
- */
- _logReset: () => LocalizedString
- }
- }
- }
- data: {
- /**
- * Showing {entryCountString}, totalling in {totalHourMin}
- */
- durationSummary: (arg: { entryCountString: string, totalHourMin: string }) => LocalizedString
- /**
- * h
- */
- hourSingleChar: () => LocalizedString
- /**
- * m
- */
- minSingleChar: () => LocalizedString
- /**
- * entry
- */
- entry: () => LocalizedString
- /**
- * entries
- */
- entries: () => LocalizedString
- /**
- * Are you sure you want to delete this entry?
- */
- confirmDeleteEntry: () => LocalizedString
- /**
- * Edit entry
- */
- editEntry: () => LocalizedString
- /**
- * Date
- */
- date: () => LocalizedString
- /**
- * From
- */
- from: () => LocalizedString
- /**
- * Duration
- */
- duration: () => LocalizedString
- /**
- * Category
- */
- category: () => LocalizedString
- /**
- * Description
- */
- description: () => LocalizedString
- /**
- * Loading
- */
- loading: () => LocalizedString
- /**
- * No entries
- */
- noEntries: () => LocalizedString
- /**
- * to
- */
- to: () => LocalizedString
- /**
- * Use
- */
- use: () => LocalizedString
- }
- home: {
- /**
- * Are you sure you want to delete this entry?
- */
- confirmDeleteEntry: () => LocalizedString
- /**
- * New entry
- */
- newEntry: () => LocalizedString
- /**
- * Edit entry
- */
- editEntry: () => LocalizedString
- /**
- * Delete entry
- */
- deleteEntry: () => LocalizedString
- /**
- * Logged time today
- */
- loggedTimeToday: () => LocalizedString
- /**
- * {hours}h{minutes}m
- */
- loggedTimeTodayString: (arg: { hours: unknown, minutes: unknown }) => LocalizedString
- /**
- * Current time
- */
- currentTime: () => LocalizedString
- /**
- * Loading
- */
- loading: () => LocalizedString
- /**
- * Stopwatch
- */
- stopwatch: () => LocalizedString
- /**
- * Today's entries
- */
- todayEntries: () => LocalizedString
- /**
- * No entries today
- */
- noEntriesToday: () => LocalizedString
- /**
- * Refresh today's entries
- */
- refreshTodayEntries: () => LocalizedString
- /**
- * Category
- */
- category: () => LocalizedString
- /**
- * Timespan
- */
- timespan: () => LocalizedString
- }
- messages: {
- /**
- * Page not found
- */
- pageNotFound: () => LocalizedString
- /**
- * Go to frontpage
- */
- goToFrontpage: () => LocalizedString
- /**
- * It seems like your device does not have a internet connection, please check your connection.
- */
- noInternet: () => LocalizedString
- }
-}
-
-export type Formatters = {}
diff --git a/old-apps/projects/src/app/lib/i18n/i18n-util.async.ts b/old-apps/projects/src/app/lib/i18n/i18n-util.async.ts
deleted file mode 100644
index 3ccef5f..0000000
--- a/old-apps/projects/src/app/lib/i18n/i18n-util.async.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
-/* eslint-disable */
-
-import { initFormatters } from './formatters'
-import type { Locales, Translations } from './i18n-types'
-import { loadedFormatters, loadedLocales, locales } from './i18n-util'
-
-const localeTranslationLoaders = {
- en: () => import('./en'),
- nb: () => import('./nb'),
-}
-
-const updateDictionary = (locale: Locales, dictionary: Partial<Translations>) =>
- loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary }
-
-export const importLocaleAsync = async (locale: Locales) =>
- (await localeTranslationLoaders[locale]()).default as unknown as Translations
-
-export const loadLocaleAsync = async (locale: Locales): Promise<void> => {
- updateDictionary(locale, await importLocaleAsync(locale))
- loadFormatters(locale)
-}
-
-export const loadAllLocalesAsync = (): Promise<void[]> => Promise.all(locales.map(loadLocaleAsync))
-
-export const loadFormatters = (locale: Locales): void =>
- void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts b/old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts
deleted file mode 100644
index f1a8e9e..0000000
--- a/old-apps/projects/src/app/lib/i18n/i18n-util.sync.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
-/* eslint-disable */
-
-import { initFormatters } from './formatters'
-import type { Locales, Translations } from './i18n-types'
-import { loadedFormatters, loadedLocales, locales } from './i18n-util'
-
-import en from './en'
-import nb from './nb'
-
-const localeTranslations = {
- en,
- nb,
-}
-
-export const loadLocale = (locale: Locales): void => {
- if (loadedLocales[locale]) return
-
- loadedLocales[locale] = localeTranslations[locale] as unknown as Translations
- loadFormatters(locale)
-}
-
-export const loadAllLocales = (): void => locales.forEach(loadLocale)
-
-export const loadFormatters = (locale: Locales): void =>
- void (loadedFormatters[locale] = initFormatters(locale))
diff --git a/old-apps/projects/src/app/lib/i18n/i18n-util.ts b/old-apps/projects/src/app/lib/i18n/i18n-util.ts
deleted file mode 100644
index cad1e7a..0000000
--- a/old-apps/projects/src/app/lib/i18n/i18n-util.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
-/* eslint-disable */
-
-import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n'
-import type { LocaleDetector } from 'typesafe-i18n/detectors'
-import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors'
-import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types'
-
-export const baseLocale: Locales = 'en'
-
-export const locales: Locales[] = [
- 'en',
- 'nb'
-]
-
-export const loadedLocales = {} as Record<Locales, Translations>
-
-export const loadedFormatters = {} as Record<Locales, Formatters>
-
-export const i18nString = (locale: Locales) => initI18nString<Locales, Formatters>(locale, loadedFormatters[locale])
-
-export const i18nObject = (locale: Locales) =>
- initI18nObject<Locales, Translations, TranslationFunctions, Formatters>(
- locale,
- loadedLocales[locale],
- loadedFormatters[locale]
- )
-
-export const i18n = () => initI18n<Locales, Translations, TranslationFunctions, Formatters>(loadedLocales, loadedFormatters)
-
-export const detectLocale = (...detectors: LocaleDetector[]) => detectLocaleFn<Locales>(baseLocale, locales, ...detectors)
diff --git a/old-apps/projects/src/app/lib/i18n/nb/index.ts b/old-apps/projects/src/app/lib/i18n/nb/index.ts
deleted file mode 100644
index 1638345..0000000
--- a/old-apps/projects/src/app/lib/i18n/nb/index.ts
+++ /dev/null
@@ -1,126 +0,0 @@
-import type {Translation} from "../i18n-types";
-
-const nb: Translation = {
- nav: {
- home: "Hjem",
- data: "Data",
- settings: "Innstillinger",
- usermenu: {
- logout: "Logg ut",
- logoutTitle: "Logg ut av din profil",
- profile: "Profil",
- profileTitle: "Administrer din profil",
- toggleTitle: "Vis brukermeny"
- }
- },
- views: {
- categoryForm: {
- name: "Navn",
- color: "Farge",
- defaultLabels: "Standard merknader",
- labelsPlaceholder: "Søk eller opprett"
- },
- dataTablePaginator: {
- goToPrevPage: "GÃ¥ til forrige side",
- goToNextPage: "GÃ¥ til neste side",
- of: "av",
- },
- settingsCategoriesTile: {
- deleteAllConfirm: "Er du sikker på at du vil slette denne kategorien?\nDette vil slette alle tilhørende rader!",
- active: "Aktive",
- archived: "Arkiverte",
- name: "Navn",
- color: "Farge",
- editEntry: "Rediger kategori",
- deleteEntry: "Slett kategori",
- noCategories: "Ingen kategorier",
- categories: "Kategorier"
- },
- settingsLabelsTile: {
- deleteAllConfirm: "Er du sikker på at du vil slette denne merknaden?\nDen vil bli slette fra alle relaterte rader!",
- active: "Aktive",
- archived: "Arkiverte",
- name: "Navn",
- color: "Farge",
- editEntry: "Rediger merknad",
- deleteEntry: "Slett merknad",
- noLabels: "Ingen merknader",
- labels: "Merknader"
- },
- entryForm: {
- entryUpdateError: "En feil oppstod med lagringen av din rad, prøv igjen snart.",
- entryCreateError: "En feil oppstod med opprettelsen av din rad, prøv igjen snart.",
- errDescriptionReq: "Beskrivelse er påkrevd",
- reset: "Tilbakestill",
- description: "Beskrivelse",
- save: "Lagre",
- create: "Opprett",
- category: {
- category: "Kategori",
- placeholder: "Søk eller opprett",
- noResults: "Ingen kategorier tilgjengelig (Opprett en ny ved å skrive navnet i søkefeltet).",
- errisRequired: "Kategori er påkrevd",
- _logReset: "Tilbakestilte kategori-seksjonen"
- },
- labels: {
- placeholder: "Søk eller opprett",
- noResults: "Ingen merkander tilgjengelig (Opprett en ny ved å skrive navnet i søkefeltet).",
- labels: "Merknader",
- _logReset: "Tilbakestilte merknader-seksjonen"
- },
- dateTime: {
- errDateIsRequired: "Dato er påkrevd",
- errFromIsRequired: "Fra er påkrevd",
- errFromAfterTo: "Fra kan ikke være etter Til",
- errFromEqTo: "Fra og Til kan ikke ha lik verdi",
- errToIsRequired: "Til er påkrevd",
- errToBeforeFrom: "Til kan ikke være før Fra",
- from: "Fra",
- to: "Til",
- date: "Dato",
- _logReset: "Tilbakestilte dato-seksjonen"
- }
- }
- },
- data: {
- durationSummary: "Viser {entryCountString:string}, Tilsammen {totalHourMin:string}",
- hourSingleChar: "t",
- minSingleChar: "m",
- entry: "rad",
- entries: "rader",
- confirmDeleteEntry: "Er du sikker på at du vil slette denne raden?",
- editEntry: "Rediger rad",
- date: "Dato",
- from: "Fra",
- duration: "Tidsrom",
- category: "Kategori",
- description: "Beskrivelse",
- loading: "Laster",
- noEntries: "Ingen rader",
- to: "til",
- use: "Bruk",
- },
- home: {
- loggedTimeTodayString: "{hours}t{minutes}m",
- confirmDeleteEntry: "Er du sikker på at du vil slette denne raden?",
- newEntry: "Ny tidsoppføring",
- editEntry: "Rediger rad",
- deleteEntry: "Slett rad",
- loggedTimeToday: "Registrert tid hittil idag",
- currentTime: "Klokken",
- loading: "Laster",
- stopwatch: "Stoppeklokke",
- todayEntries: "Dagens tidsoppføringer",
- noEntriesToday: "Ingen oppføringer i dag",
- refreshTodayEntries: "Last inn dagens tidsoppføringer på nytt",
- category: "Kategori",
- timespan: "Tidsrom",
- },
- messages: {
- pageNotFound: "Fant ikke siden",
- goToFrontpage: "GÃ¥ til forsiden",
- noInternet: "Det ser ut som at du er uten internettilgang, vennligst sjekk tilkoblingen din."
- }
-};
-
-export default nb;
diff --git a/old-apps/projects/src/app/lib/services/user-service.ts b/old-apps/projects/src/app/lib/services/user-service.ts
deleted file mode 100644
index 4155819..0000000
--- a/old-apps/projects/src/app/lib/services/user-service.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import {portal_base} from "$shared/lib/configuration";
-import {end_session} from "$shared/lib/session";
-import {clear_categories} from "$app/lib/stores/categories";
-import {clear_entries} from "$app/lib/stores/entries";
-import {clear_labels} from "$app/lib/stores/labels";
-
-export async function logout_user(reason: string = "") {
- await end_session(() => {
- clear_categories();
- clear_labels();
- clear_entries();
- location.replace(portal_base("#/login" + (reason ? "?" + reason : "")));
- });
-}
diff --git a/old-apps/projects/src/app/lib/stores/categories.ts b/old-apps/projects/src/app/lib/stores/categories.ts
deleted file mode 100644
index 2a63c42..0000000
--- a/old-apps/projects/src/app/lib/stores/categories.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {writable, get} from "svelte/store";
-import {create_time_category, delete_time_category, get_time_categories} from "$shared/lib/api/time-entry";
-import type {TimeCategoryDto} from "$shared/lib/models/TimeCategoryDto";
-import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse";
-
-const categories = writable<Array<TimeCategoryDto>>([]);
-
-export async function reload_categories() {
- const get_categories_response = await get_time_categories();
- if (!get_categories_response.ok) {
- clear_categories();
- return;
- }
- categories.set(get_categories_response.data ?? []);
-}
-
-export function clear_categories() {
- categories.set([]);
-}
-
-export async function create_category_async(request: TimeCategoryDto): Promise<IInternalFetchResponse> {
- const create_entry_response = await create_time_category(request);
- if (create_entry_response.ok) {
- const stored_entries = get(categories);
- stored_entries.push(create_entry_response.data);
- categories.set(stored_entries);
- }
- return create_entry_response;
-}
-
-export async function edit_category_async(entry: TimeCategoryDto) {
- if (!entry.id) return;
-}
-
-export async function delete_category_async(entry: TimeCategoryDto) {
- if (!entry.id) return;
- const http_request = await delete_time_category(entry.id);
- if (http_request.ok) {
- const stored_entries = get(categories);
- categories.set(stored_entries.filter(e => e.id !== entry.id));
- }
-}
-
-export default categories;
diff --git a/old-apps/projects/src/app/lib/stores/entries.ts b/old-apps/projects/src/app/lib/stores/entries.ts
deleted file mode 100644
index e933568..0000000
--- a/old-apps/projects/src/app/lib/stores/entries.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import {Temporal} from "@js-temporal/polyfill";
-import {writable, get} from "svelte/store";
-import {get_time_entries, create_time_entry, delete_time_entry, update_time_entry} from "$shared/lib/api/time-entry";
-import type {TimeEntryDto} from "$shared/lib/models/TimeEntryDto";
-import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse";
-import type {TimeEntryQuery} from "$shared/lib/models/TimeEntryQuery";
-
-const entries = writable<Array<TimeEntryDto>>([]);
-
-export function get_time_entry(id: string): TimeEntryDto {
- return get(entries).find(c => c.id === id);
-}
-
-export async function reload_entries(query: TimeEntryQuery): Promise<void> {
- const get_entries_response = await get_time_entries(query);
- if (!get_entries_response.ok) {
- clear_entries();
- return;
- }
- entries.set(get_default_sorted(get_entries_response.data?.results ?? []));
-}
-
-export function clear_entries() {
- entries.set([]);
-}
-
-function get_default_sorted(unsorted: Array<TimeEntryDto>): Array<TimeEntryDto> {
- if (unsorted.length < 1) return unsorted;
- const byStart = unsorted.sort((a, b) => {
- return Temporal.Instant.compare(Temporal.Instant.from(b.start), Temporal.Instant.from(a.start));
- });
-
- return byStart.sort((a, b) => {
- return Temporal.Instant.compare(Temporal.Instant.from(b.stop), Temporal.Instant.from(a.stop));
- });
-}
-
-export async function create_entry_async(request: TimeEntryDto): Promise<IInternalFetchResponse> {
- const create_entry_response = await create_time_entry(request);
- if (create_entry_response.ok) {
- const stored_entries = get(entries) ?? [];
- stored_entries.push(create_entry_response.data);
- entries.set(get_default_sorted(stored_entries));
- }
- return create_entry_response;
-}
-
-export async function edit_entry_async(request: TimeEntryDto): Promise<IInternalFetchResponse> {
- if (!request.id) return;
- const edit_entry_response = await update_time_entry(request);
- if (edit_entry_response.ok) {
- const stored_entries = get(entries) ?? [];
- const index = stored_entries.findIndex(c => c.id === request.id);
- if (index === -1) {
- stored_entries.push(edit_entry_response.data);
- } else {
- stored_entries[index] = edit_entry_response.data;
- }
- entries.set(get_default_sorted(stored_entries));
- }
- return edit_entry_response;
-}
-
-export async function delete_entry_async(entry_id: string): Promise<void> {
- if (!entry_id) throw new Error("No id was supplied when deleting query");
- const delete_entry_response = await delete_time_entry(entry_id);
- if (delete_entry_response.ok) {
- const stored_entries = get(entries) ?? [];
- entries.set(get_default_sorted(stored_entries.filter((e) => e.id !== entry_id) ?? []));
- }
-}
-
-
-export default entries;
diff --git a/old-apps/projects/src/app/lib/stores/labels.ts b/old-apps/projects/src/app/lib/stores/labels.ts
deleted file mode 100644
index d5ffaa9..0000000
--- a/old-apps/projects/src/app/lib/stores/labels.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {writable, get} from "svelte/store";
-import {create_time_label, delete_time_label, get_time_labels} from "$shared/lib/api/time-entry";
-import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse";
-import type {TimeLabelDto} from "$shared/lib/models/TimeLabelDto";
-
-const labels = writable<Array<TimeLabelDto>>([]);
-
-export async function reload_labels() {
- const get_labels_response = await get_time_labels();
- if (!get_labels_response.ok) {
- clear_labels();
- return;
- }
- labels.set(get_labels_response.data ?? []);
-}
-
-export function clear_labels() {
- labels.set([]);
-}
-
-export async function create_label_async(request: TimeLabelDto): Promise<IInternalFetchResponse> {
- const create_label_response = await create_time_label(request);
- if (create_label_response.ok) {
- const stored_entries = get(labels) ?? [];
- stored_entries.push(create_label_response.data);
- labels.set(stored_entries);
- }
- return create_label_response;
-}
-
-export async function edit_label_async(entry: TimeLabelDto) {
- if (!entry.id) throw new Error("Label id is required");
-}
-
-export async function delete_label_async(entry: TimeLabelDto) {
- if (!entry.id) return;
- const http_request = await delete_time_label(entry.id);
- if (http_request.ok) {
- const stored_entries = get(labels) ?? [];
- labels.set(stored_entries.filter(e => e.id !== entry.id));
- }
-}
-
-export default labels;
diff --git a/old-apps/projects/src/app/pages/_layout.svelte b/old-apps/projects/src/app/pages/_layout.svelte
deleted file mode 100644
index 07a4a25..0000000
--- a/old-apps/projects/src/app/pages/_layout.svelte
+++ /dev/null
@@ -1,66 +0,0 @@
-<script>
- import {onMount} from "svelte";
- import {location, link} from "svelte-spa-router";
- import {logout_user} from "$app/lib/services/user-service";
- import {random_string} from "$shared/lib/helpers";
- import {get_session_data} from "$shared/lib/session";
- import ProfileModal from "$app/pages/views/profile-modal.svelte";
- import {Menu, MenuItem, MenuItemSeparator} from "$shared/components/menu";
- import Button from "$shared/components/button.svelte";
- import {IconNames} from "$shared/lib/configuration";
- import LL from "$app/lib/i18n/i18n-svelte";
- import BlowoutToolbelt from "$shared/components/blowout-toolbelt.svelte";
- import {NavWrapper, NavItem} from "./nav";
-
- let ProfileModalFunctions = {};
- let showUserMenu = false;
- let userMenuTriggerNode;
- const userMenuId = "__menu_" + random_string(3);
- const username = get_session_data()?.profile.username;
-
- onMount(() => {
- userMenuTriggerNode = document.getElementById("open-user-menu");
- });
-</script>
-
-<ProfileModal bind:functions={ProfileModalFunctions}/>
-<BlowoutToolbelt/>
-
-<NavWrapper>
- <ul slot="navigation-items">
- <NavItem to="/home" text="{$LL.nav.home()}"/>
- <NavItem to="/data" text="{$LL.nav.data()}"/>
- <NavItem to="/settings" text="{$LL.nav.settings()}"/>
- </ul>
- <div slot="navigation-footer" class="tabs-nav-v2 justify-between">
- <div class="tab-v2 padding-x-sm">
- <Button class="user-menu-control"
- variant="reset"
- id="open-user-menu"
- on:click={() => showUserMenu = !showUserMenu}
- text={username}
- icon={IconNames.chevronDown}
- icon_width="2rem"
- icon_height="2rem"
- icon_right_aligned="true"
- title="{$LL.nav.usermenu.toggleTitle()}"
- aria-controls="{userMenuId}"
- />
- <Menu bind:show="{showUserMenu}"
- trigger={userMenuTriggerNode}
- id="{userMenuId}">
- <div slot="options">
- <MenuItem on:click={() => ProfileModalFunctions.open()}>
- <span title="{$LL.nav.usermenu.profileTitle()}">{$LL.nav.usermenu.profile()}</span>
- </MenuItem>
- <MenuItemSeparator/>
- <MenuItem danger="true"
- on:click={() => logout_user()}>
- <span title="{$LL.nav.usermenu.logoutTitle()}">{$LL.nav.usermenu.logout()}</span>
- </MenuItem>
- </div>
- </Menu>
- </div>
- </div>
- <slot slot="main-content"/>
-</NavWrapper> \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/data.svelte b/old-apps/projects/src/app/pages/data.svelte
deleted file mode 100644
index 190c641..0000000
--- a/old-apps/projects/src/app/pages/data.svelte
+++ /dev/null
@@ -1,396 +0,0 @@
-<script>
- import {IconNames} from "$shared/lib/configuration";
- import {onMount} from "svelte";
- import {Temporal} from "@js-temporal/polyfill";
- import Layout from "./_layout.svelte";
- import Modal from "$shared/components/modal.svelte";
- import Tile from "$shared/components/tile.svelte";
- import Icon from "$shared/components/icon.svelte";
- import EntryForm from "$app/pages/views/entry-form/index.svelte";
- import {Table, THead, TBody, TCell, TRow, TablePaginator} from "$shared/components/table";
- import {TimeEntryQueryDuration} from "$shared/lib/models/TimeEntryQuery";
- import {delete_time_entry, get_time_entries, get_time_entry} from "$shared/lib/api/time-entry";
- import {seconds_to_hour_minute_string, is_guid, move_focus, unwrap_date_time_from_entry} from "$shared/lib/helpers";
- import Button from "$shared/components/button.svelte";
- import LL from "$app/lib/i18n/i18n-svelte";
-
- let pageCount = 1;
- let page = 1;
-
- const defaultQuery = {
- duration: TimeEntryQueryDuration.THIS_YEAR,
- categories: [],
- labels: [],
- page: page,
- pageSize: 50,
- };
-
- let isLoading;
- let categories = [];
- let labels = [];
- let entries = [];
- let durationSummary = false;
- let EditEntryModal;
- let EditEntryForm;
- let currentTimespanFilter = TimeEntryQueryDuration.THIS_YEAR;
- let currentSpecificDateFilter = Temporal.Now.plainDateTimeISO().subtract({days: 1}).toString().substring(0, 10);
- let currentDateRangeFilter = {};
- let currentCategoryFilter = "all";
- let currentLabelFilter = "all";
- let showDateFilterOptions = false;
- let secondsLogged = 0;
-
- function set_duration_summary_string() {
- if (entries.length > 0) {
- durationSummary = $LL.data.durationSummary({
- entryCountString: `${entries.length} ${entries.length === 1 ? $LL.data.entry() : $LL.data.entries()}`,
- totalHourMin: seconds_to_hour_minute_string(secondsLogged)
- });
- } else {
- durationSummary = "";
- }
- }
-
- async function load_entries(query = defaultQuery) {
- isLoading = true;
- const response = await get_time_entries(query);
- if (response.status === 200) {
- const responseEntries = [];
- secondsLogged = 0;
- for (const entry of response.data.results) {
- const date_time = unwrap_date_time_from_entry(entry);
- const seconds = (date_time.duration.hours * 60 * 60) + (date_time.duration.minutes * 60);
- responseEntries.push({
- id: entry.id,
- date: date_time.start_date,
- start: date_time.start_time,
- stop: date_time.stop_time,
- durationString: date_time.duration.hours + $LL.data.hourSingleChar() + date_time.duration.minutes + $LL.data.minSingleChar(),
- seconds: seconds,
- category: entry.category,
- labels: entry.labels,
- description: entry.description,
- });
- secondsLogged += seconds;
- }
- entries = responseEntries;
- page = response.data.page;
- pageCount = response.data.totalPageCount;
- } else {
- entries = [];
- page = 0;
- pageCount = 0;
- }
- isLoading = false;
- set_duration_summary_string();
- }
-
- function load_entries_with_filter(page = 1) {
- let query = defaultQuery;
- query.duration = currentTimespanFilter;
- query.labels = [];
- query.categories = [];
- query.page = page;
-
- if (currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE) {
- query.specificDate = currentSpecificDateFilter;
- } else {
- delete query.specificDate;
- }
-
- if (currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE) {
- query.dateRange = currentDateRangeFilter;
- } else {
- delete query.dateRange;
- }
-
- if ((currentCategoryFilter !== "all" && currentCategoryFilter?.length > 0) ?? false) {
- for (const chosenCategoryId of currentCategoryFilter) {
- if (chosenCategoryId === "all") {
- continue;
- }
- query.categories.push({
- id: chosenCategoryId,
- });
- }
- }
-
- if ((currentLabelFilter !== "all" && currentLabelFilter?.length > 0) ?? false) {
- for (const chosenLabelId of currentLabelFilter) {
- if (chosenLabelId === "all") {
- continue;
- }
- query.labels.push({
- id: chosenLabelId,
- });
- }
- }
-
- load_entries(query);
- }
-
- async function handle_delete_entry_button_click(e, entryId) {
- if (confirm($LL.data.confirmDeleteEntry())) {
- const response = await delete_time_entry(entryId);
- if (response.ok) {
- const indexOfEntry = entries.findIndex((c) => c.id === entryId);
- if (indexOfEntry !== -1) {
- secondsLogged -= entries[indexOfEntry].seconds;
- entries.splice(indexOfEntry, 1);
- entries = entries;
- set_duration_summary_string();
- }
- }
- }
- }
-
- function handle_edit_entry_form_updated() {
- load_entries_with_filter(page);
- EditEntryModal.close();
- }
-
- async function handle_edit_entry_button_click(event, entryId) {
- const response = await get_time_entry(entryId);
- if (response.status === 200) {
- if (is_guid(response.data.id)) {
- EditEntryForm.set_values(response.data);
- EditEntryModal.open();
- move_focus(document.querySelector("input[id='date']"));
- }
- }
- }
-
- function close_date_filter_box(event) {
- if (!event.target.closest(".date_filter_box_el")) {
- showDateFilterOptions = false;
- window.removeEventListener("click", close_date_filter_box);
- }
- }
-
- function toggle_date_filter_box(event) {
- const box = document.getElementById("date_filter_box");
- const rect = event.target.getBoundingClientRect();
- box.style.top = rect.y + "px";
- box.style.left = rect.x - 50 + "px";
- showDateFilterOptions = true;
- window.addEventListener("click", close_date_filter_box);
- }
-
- onMount(() => {
- isLoading = true;
- Promise.all([load_entries()]).then(() => {
- isLoading = false;
- });
- });
-</script>
-
-<Modal title="{$LL.data.editEntry()}"
- bind:functions={EditEntryModal}
- on:closed={() => EditEntryForm.reset()}>
- <EntryForm bind:functions={EditEntryForm}
- on:updated={handle_edit_entry_form_updated}/>
-</Modal>
-
-<div id="date_filter_box"
- style="margin-top:25px"
- class="padding-xs z-index-overlay bg shadow-sm position-absolute date_filter_box_el border {showDateFilterOptions ? '' : 'hide'}">
- <div class="flex items-baseline margin-bottom-xxxxs">
- <label class="text-sm color-contrast-medium margin-right-xs"
- for="durationSelect">Timespan:</label>
- <div class="select inline-block js-select">
- <select name="durationSelect"
- bind:value={currentTimespanFilter}
- id="durationSelect">
- <option value={TimeEntryQueryDuration.TODAY}
- selected> Today
- </option>
- <option value={TimeEntryQueryDuration.THIS_WEEK}>This week</option>
- <option value={TimeEntryQueryDuration.THIS_MONTH}>This month</option>
- <option value={TimeEntryQueryDuration.THIS_YEAR}>This year</option>
- <option value={TimeEntryQueryDuration.SPECIFIC_DATE}>Spesific date</option>
- <option value={TimeEntryQueryDuration.DATE_RANGE}>Date range</option>
- </select>
-
- <svg class="icon icon--xxxs margin-left-xxs"
- viewBox="0 0 8 8">
- <path d="M7.934,1.251A.5.5,0,0,0,7.5,1H.5a.5.5,0,0,0-.432.752l3.5,6a.5.5,0,0,0,.864,0l3.5-6A.5.5,0,0,0,7.934,1.251Z"/>
- </svg>
- </div>
- </div>
-
- {#if currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE}
- <div class="flex items-baseline margin-bottom-xxxxs justify-between">
- <span class="text-sm color-contrast-medium margin-right-xs">{$LL.data.date()}:</span>
- <span class="text-sm">
- <input type="date"
- class="border-none padding-0 color-inherit bg-transparent"
- bind:value={currentSpecificDateFilter}/>
- </span>
- </div>
- {/if}
-
- {#if currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE}
- <div class="flex items-baseline margin-bottom-xxxxs justify-between">
- <span class="text-sm color-contrast-medium margin-right-xs">{$LL.data.from()}:</span>
- <span class="text-sm">
- <input type="date"
- class="border-none padding-0 color-inherit bg-transparent"
- on:change={(e) => (currentDateRangeFilter.from = e.target.value)}/>
- </span>
- </div>
-
- <div class="flex items-baseline margin-bottom-xxxxs justify-between">
- <span class="text-sm color-contrast-medium margin-right-xs">{$LL.data.to()}:</span>
- <span class="text-sm">
- <input type="date"
- class="border-none padding-0 color-inherit bg-transparent"
- on:change={(e) => (currentDateRangeFilter.to = e.target.value)}/>
- </span>
- </div>
- {/if}
-
- <div class="flex items-baseline justify-end">
- <Button variant="subtle"
- on:click={() => load_entries_with_filter(page)}
- class="text-sm"
- text="{$LL.data.use()}"/>
- </div>
-</div>
-
-<Layout>
- <Tile class="{isLoading ? 'c-disabled loading' : ''}">
- <nav class="s-tabs text-sm hide">
- <ul class="s-tabs__list">
- <li><span class="s-tabs__link s-tabs__link--current">All (21)</span></li>
- <li><span class="s-tabs__link">Published (19)</span></li>
- <li><span class="s-tabs__link">Draft (2)</span></li>
- </ul>
- </nav>
- <div class="max-width-100% overflow-auto"
- style="max-height: 82.5vh">
- <Table class="text-sm width-100% int-table--sticky-header">
- <THead>
- <TCell type="th"
- style="width: 30px;">
- <div class="custom-checkbox int-table__checkbox">
- <input class="custom-checkbox__input"
- type="checkbox"
- aria-label="Select all rows"/>
- <div class="custom-checkbox__control"
- aria-hidden="true"></div>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 100px">
- <div class="flex items-center justify-between">
- <span>{$LL.data.date()}</span>
- <div class="date_filter_box_el cursor-pointer"
- on:click={toggle_date_filter_box}>
- <Icon name="{IconNames.funnel}"/>
- </div>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 100px">
- <div class="flex items-center">
- <span>{$LL.data.duration()}</span>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 100px;">
- <div class="flex items-center">
- <span>{$LL.data.category()}</span>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 300px;">
- <div class="flex items-center">
- <span>{$LL.data.description()}</span>
- </div>
- </TCell>
- <TCell type="th"
- style="width: 50px"></TCell>
- </THead>
- <TBody>
- {#if entries.length > 0}
- {#each entries as entry}
- <TRow class="text-nowrap"
- data-id={entry.id}>
- <TCell type="th"
- thScope="row">
- <div class="custom-checkbox int-table__checkbox">
- <input class="custom-checkbox__input"
- type="checkbox"
- aria-label="Select this row"/>
- <div class="custom-checkbox__control"
- aria-hidden="true"></div>
- </div>
- </TCell>
- <TCell>
- <pre>{entry.date.toLocaleString()}</pre>
- </TCell>
- <TCell>
- <pre class="flex justify-between">
- <div class="flex justify-between">
- <span>{entry.start.toLocaleString(undefined, {timeStyle: "short"})}</span>
- <span> - </span>
- <span>{entry.stop.toLocaleString(undefined, {timeStyle: "short"})}</span>
- </div>
- </pre>
- </TCell>
- <TCell>
- <span data-id={entry.category.id}>{entry.category.name}</span>
- </TCell>
- <TCell class="text-truncate max-width-xxxxs"
- title="{entry.description}">
- {entry.description ?? ""}
- </TCell>
- <TCell class="flex flex-row justify-end items-center">
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => handle_edit_entry_button_click(e, entry.id)}
- title="Edit entry"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => handle_delete_entry_button_click(e, entry.id)}
- title="Delete entry"/>
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow class="text-nowrap">
- <TCell type="th"
- thScope="row"
- colspan="7">
- {isLoading ? $LL.data.loading() + "..." : $LL.data.noEntries()}
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
- <div class="flex items-center justify-between">
- <p class="text-sm">
- {#if durationSummary}
- <small class={isLoading ? "c-disabled loading" : ""}>{durationSummary}</small>
- {:else}
- <small class={isLoading ? "c-disabled loading" : ""}>{$LL.data.noEntries()}</small>
- {/if}
- </p>
-
- <nav class="grid padding-y-sm {isLoading ? 'c-disabled loading' : ''}">
- <TablePaginator {page}
- on:value_change={(e) => load_entries_with_filter(e.detail.newValue)}
- {pageCount}/>
- </nav>
- </div>
- </Tile>
-</Layout>
diff --git a/old-apps/projects/src/app/pages/home.svelte b/old-apps/projects/src/app/pages/home.svelte
deleted file mode 100644
index 1f398b5..0000000
--- a/old-apps/projects/src/app/pages/home.svelte
+++ /dev/null
@@ -1,178 +0,0 @@
-<script lang="ts">
- import LL from "$app/lib/i18n/i18n-svelte";
- import { delete_time_entry, get_time_entries, get_time_entry } from "$shared/lib/api/time-entry";
- import { IconNames, QueryKeys } from "$shared/lib/configuration";
- import { TimeEntryDto } from "$shared/lib/models/TimeEntryDto";
- import { Temporal } from "@js-temporal/polyfill";
- import { useMutation, useQuery, useQueryClient } from "@sveltestack/svelte-query";
- import { onMount } from "svelte";
- import Tile from "$shared/components/tile.svelte";
- import Button from "$shared/components/button.svelte";
- import Stopwatch from "$shared/components/stopwatch.svelte";
- import { Table, THead, TBody, TCell, TRow } from "$shared/components/table";
- import Layout from "./_layout.svelte";
- import EntryFrom from "$app/pages/views/entry-form/index.svelte";
- import { seconds_to_hour_minute, unwrap_date_time_from_entry } from "$shared/lib/helpers";
- import { TimeEntryQueryDuration } from "$shared/lib/models/TimeEntryQuery";
-
- let currentTime = "";
- let isLoading = false;
- let EditEntryForm: any;
- let timeEntries = [] as Array<TimeEntryDto>;
- let timeLoggedTodayString = $LL.home.loggedTimeTodayString({hours: 0, minutes: 0});
- let loggedSecondsToday = 0;
-
- const queryClient = useQueryClient();
- const queryResult = useQuery(QueryKeys.entries, async () => await get_time_entries({
- duration: TimeEntryQueryDuration.TODAY,
- page: 1,
- pageSize: 100,
- })?.data ?? []
- );
-
- function set_current_time() {
- currentTime = Temporal.Now.plainTimeISO().toLocaleString(undefined, {
- timeStyle: "short",
- });
- }
-
- const delete_entry_mutation = useMutation(delete_time_entry, {
- onSuccess: (data) => {
- queryClient.invalidateQueries([QueryKeys.entries, data.data.id]);
- },
- });
-
- async function on_edit_entry_button_click(event, entryId: string) {
- const response = useQuery([QueryKeys.entries, entryId], () => {
- return get_time_entry(entryId);
- });
-
- EditEntryForm.set_values(response);
- }
-
- async function on_delete_entry_button_click(event, entryId: string) {
- if (confirm($LL.home.confirmDeleteEntry())) {
- $delete_entry_mutation.mutate(entryId);
- }
- }
-
- function on_create_from_stopwatch(event) {
- EditEntryForm.set_time({to: event.detail.to, from: event.detail.from});
- if (event.detail.description) {
- EditEntryForm.set_description(event.detail.description);
- }
- }
-
- onMount(async () => {
- set_current_time();
- setInterval(() => {
- set_current_time();
- }, 1e4);
- queryResult.subscribe((result) => {
- const newEntries = [];
- loggedSecondsToday = 0;
- for (const entry of result.data?.results ?? []) {
- const date_time = unwrap_date_time_from_entry(entry);
- newEntries.push({
- id: entry.id,
- start: date_time.start_time,
- stop: date_time.stop_time,
- category: entry.category,
- });
- loggedSecondsToday += (date_time.duration.hours * 60 * 60) + (date_time.duration.minutes * 60);
- }
- timeLoggedTodayString = $LL.home.loggedTimeTodayString(seconds_to_hour_minute(loggedSecondsToday));
- timeEntries = newEntries;
- });
- });
-</script>
-
-<Layout>
- <div class="grid gap-md margin-top-xs flex-row@md items-start flex-column-reverse">
- <Tile class="col">
- <h3 class="text-md padding-bottom-xxxs">{$LL.home.newEntry()}</h3>
- <EntryFrom bind:functions={EditEntryForm}/>
- </Tile>
- <div class="col grid gap-sm">
- <Tile class="col-6@md col-12">
- <p class="text-xxl">{timeLoggedTodayString}</p>
- <p class="text-xs margin-bottom-xxs">{$LL.home.loggedTimeToday()}</p>
- <pre class="text-xxl">{currentTime}</pre>
- <p class="text-xs">{$LL.home.currentTime()}</p>
- </Tile>
- <Tile class="col-6@md col-12">
- <Stopwatch on:create={on_create_from_stopwatch}>
- <h3 slot="header"
- class="text-md">{$LL.home.stopwatch()}</h3>
- </Stopwatch>
- </Tile>
- <Tile class="col-12">
- <h3 class="text-md padding-bottom-xxxs">{$LL.home.todayEntries()}</h3>
- <div class="max-width-100% overflow-auto">
- <Table class="width-100% text-sm">
- <THead>
- <TCell type="th"
- class="text-left">
- <span>{$LL.home.category()}</span>
- </TCell>
- <TCell type="th"
- class="text-left">
- <span>{$LL.home.timespan()}</span>
- </TCell>
- <TCell type="th"
- class="text-right">
- <Button icon="{IconNames.refresh}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- title="{$LL.home.refreshTodayEntries()}"
- on:click={() => queryClient.invalidateQueries(QueryKeys.entries)}/>
- </TCell>
- </THead>
- <TBody>
- {#if timeEntries.length > 0}
- {#each timeEntries as entry}
- <TRow class="text-nowrap text-left"
- data-id={entry.id}>
- <TCell>
- <span data-id={entry.category?.id}>
- {entry.category?.name}
- </span>
- </TCell>
- <TCell>
- {entry.start.toLocaleString(undefined, {timeStyle: "short"})}
- <span>-</span>
- {entry.stop.toLocaleString(undefined, {timeStyle: "short"})}
- </TCell>
- <TCell class="flex flex-row justify-end items-center">
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => on_edit_entry_button_click(e, entry.id)}
- title="{$LL.home.editEntry()}"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => on_delete_entry_button_click(e, entry.id)}
- title="{$LL.home.deleteEntry()}"/>
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow class="text-nowrap">
- <TCell type="th"
- thScope="row"
- colspan="7">
- {isLoading ? $LL.home.loading() + "..." : $LL.home.noEntriesToday()}
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
- </Tile>
- </div>
- </div>
-</Layout>
diff --git a/old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css b/old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css
deleted file mode 100644
index 515a9f2..0000000
--- a/old-apps/projects/src/app/pages/nav/css/1_responsive-sidebar.css
+++ /dev/null
@@ -1,179 +0,0 @@
-/* --------------------------------
-
-File#: _1_responsive-sidebar
-Title: Responsive Sidebar
-Descr: Responsive sidebar container
-Usage: codyhouse.co/license
-
--------------------------------- */
-/* mobile version only (--default) 👇 */
-.sidebar:not(.sidebar--static) {
- position: fixed;
- top: 0;
- left: 0;
- z-index: var(--z-index-fixed-element, 10);
- width: 100%;
- height: 100%;
- visibility: hidden;
- transition: visibility 0s 0.3s;
-}
-.sidebar:not(.sidebar--static)::after {
- /* overlay layer */
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: hsla(var(--color-black-h), var(--color-black-s), var(--color-black-l), 0);
- transition: background-color 0.3s;
- z-index: 1;
-}
-.sidebar:not(.sidebar--static) .sidebar__panel {
- /* content */
- position: absolute;
- top: 0;
- left: 0;
- z-index: 2;
- width: 100%;
- max-width: 380px;
- height: 100%;
- overflow: auto;
- -webkit-overflow-scrolling: touch;
- background-color: var(--color-bg);
- -webkit-transform: translateX(-100%);
- transform: translateX(-100%);
- transition: box-shadow 0.3s, -webkit-transform 0.3s;
- transition: box-shadow 0.3s, transform 0.3s;
- transition: box-shadow 0.3s, transform 0.3s, -webkit-transform 0.3s;
-}
-.sidebar:not(.sidebar--static).sidebar--right-on-mobile .sidebar__panel {
- left: auto;
- right: 0;
- -webkit-transform: translateX(100%);
- transform: translateX(100%);
-}
-.sidebar:not(.sidebar--static).sidebar--is-visible {
- visibility: visible;
- transition: none;
-}
-.sidebar:not(.sidebar--static).sidebar--is-visible::after {
- background-color: hsla(var(--color-black-h), var(--color-black-s), var(--color-black-l), 0.85);
-}
-.sidebar:not(.sidebar--static).sidebar--is-visible .sidebar__panel {
- -webkit-transform: translateX(0);
- transform: translateX(0);
- box-shadow: var(--shadow-md);
-}
-
-/* end mobile version */
-.sidebar__header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: -webkit-sticky;
- position: sticky;
- top: 0;
-}
-
-.sidebar__close-btn {
- --size: 32px;
- width: var(--size);
- height: var(--size);
- display: flex;
- border-radius: 50%;
- background-color: var(--color-bg-light);
- box-shadow: var(--inner-glow), var(--shadow-sm);
- transition: 0.2s;
- flex-shrink: 0;
-}
-.sidebar__close-btn .icon {
- display: block;
- margin: auto;
-}
-.sidebar__close-btn:hover {
- background-color: var(--color-bg-lighter);
- box-shadow: var(--inner-glow), var(--shadow-md);
-}
-
-/* desktop version only (--static) 👇 */
-.sidebar--static {
- flex-shrink: 0;
- flex-grow: 1;
-}
-.sidebar--static .sidebar__header {
- display: none;
-}
-
-.sidebar--sticky-on-desktop {
- position: -webkit-sticky;
- position: sticky;
- top: var(--space-sm);
- max-height: calc(100vh - var(--space-sm));
- overflow: auto;
- -webkit-overflow-scrolling: touch;
-}
-
-/* end desktop version */
-.sidebar, .sidebar-loaded\:show {
- opacity: 0;
- /* hide sidebar - or other elements using the .sidebar-loaded:show class - while it is initialized in JS */
-}
-
-.sidebar--loaded {
- opacity: 1;
-}
-
-/* detect when the sidebar needs to switch from the mobile layout to a static one - used in JS */
-[class*=sidebar--static]::before {
- display: none;
-}
-
-.sidebar--static::before {
- content: "static";
-}
-
-.sidebar--static\@xs::before {
- content: "mobile";
-}
-@media (min-width: 32rem) {
- .sidebar--static\@xs::before {
- content: "static";
- }
-}
-
-.sidebar--static\@sm::before {
- content: "mobile";
-}
-@media (min-width: 48rem) {
- .sidebar--static\@sm::before {
- content: "static";
- }
-}
-
-.sidebar--static\@md::before {
- content: "mobile";
-}
-@media (min-width: 64rem) {
- .sidebar--static\@md::before {
- content: "static";
- }
-}
-
-.sidebar--static\@lg::before {
- content: "mobile";
-}
-@media (min-width: 80rem) {
- .sidebar--static\@lg::before {
- content: "static";
- }
-}
-
-.sidebar--static\@xl::before {
- content: "mobile";
-}
-@media (min-width: 90rem) {
- .sidebar--static\@xl::before {
- content: "static";
- }
-} \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css b/old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css
deleted file mode 100644
index ec5fcdf..0000000
--- a/old-apps/projects/src/app/pages/nav/css/2_side-navigation-v4.css
+++ /dev/null
@@ -1,213 +0,0 @@
-/* --------------------------------
-
-File#: _2_side-navigation-v4
-Title: Side Navigation v4
-Descr: Main, side navigation
-Usage: codyhouse.co/license
-
--------------------------------- */
-.sidenav-v4 {
- --sidenav-v4-icon-size: 20px;
- --sidenav-v4-icon-margin-right: var(--space-xxs);
-}
-
-.sidenav-v4__item {
- position: relative;
-}
-
-.sidenav-v4__link,
-.sidenav-v4__sub-link,
-.sidenav-v4__separator {
- padding: var(--space-sm);
-}
-
-.sidenav-v4__link, .sidenav-v4__sub-link {
- display: flex;
- align-items: center;
- width: 100%;
- border-radius: var(--radius-md);
- text-decoration: none;
- color: inherit;
- line-height: 1;
- font-size: var(--text-md);
- transition: 0.2s;
-}
-.sidenav-v4__link:hover, .sidenav-v4__sub-link:hover {
- color: var(--color-primary);
- background-color: hsla(var(--color-contrast-higher-h), var(--color-contrast-higher-s), var(--color-contrast-higher-l), 0.075);
-}
-.sidenav-v4__link[aria-current=page], .sidenav-v4__sub-link[aria-current=page] {
- color: var(--color-primary);
-}
-
-.sidenav-v4__sub-link {
- position: relative;
- color: var(--color-contrast-medium);
- /* dot indicator */
-}
-.sidenav-v4__sub-link::before {
- content: "";
- display: block;
- --size: 6px;
- width: var(--size);
- height: var(--size);
- background: currentColor;
- border-radius: 50%;
- margin-left: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2);
- margin-right: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2 + var(--sidenav-v4-icon-margin-right));
- opacity: 0;
- /* visible only if current */
-}
-.sidenav-v4__sub-link[aria-current=page]::before {
- /* show dot indicator */
- opacity: 1;
-}
-
-.sidenav-v4__notification-marker {
- margin-left: auto;
- background-color: var(--color-accent);
- border-radius: var(--radius-md);
- height: 16px;
- line-height: 16px;
- padding: 0 4px;
- color: var(--color-white);
- font-size: 12px;
- /* hide - visible only on desktop */
- display: none;
-}
-
-/* label icon */
-.sidenav-v4__icon {
- --size: var(--sidenav-v4-icon-size);
- margin-right: var(--sidenav-v4-icon-margin-right);
-}
-
-/* arrow icon - visible on mobile if item is expandable */
-.sidenav-v4__arrow-icon {
- --size: 20px;
- /* hide icon for links - show only for buttons created in JS */
-}
-.sidenav-v4__arrow-icon .icon__group {
- will-change: transform;
- -webkit-transform-origin: 50% 50%;
- transform-origin: 50% 50%;
- -webkit-transform: rotate(-90deg);
- transform: rotate(-90deg);
- transition: -webkit-transform 0.3s var(--ease-out);
- transition: transform 0.3s var(--ease-out);
- transition: transform 0.3s var(--ease-out), -webkit-transform 0.3s var(--ease-out);
-}
-.sidenav-v4__arrow-icon .icon__group > * {
- -webkit-transform-origin: 50% 50%;
- transform-origin: 50% 50%;
- stroke-dasharray: 20;
- stroke-dashoffset: 0;
- -webkit-transform: translateY(0px);
- transform: translateY(0px);
- transition: stroke-dashoffset 0.3s, -webkit-transform 0.3s;
- transition: transform 0.3s, stroke-dashoffset 0.3s;
- transition: transform 0.3s, stroke-dashoffset 0.3s, -webkit-transform 0.3s;
- transition-timing-function: var(--ease-out);
-}
-.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
-}
-.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group > * {
- -webkit-transform: translateY(4px);
- transform: translateY(4px);
-}
-.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group > *:first-child {
- stroke-dashoffset: 10.15;
-}
-.sidenav-v4__item--collapsed .sidenav-v4__arrow-icon .icon__group > *:last-child {
- stroke-dashoffset: 10.15;
-}
-.sidenav-v4__link--href .sidenav-v4__arrow-icon {
- display: none;
-}
-
-/* current item */
-.sidenav-v4__item--current .sidenav-v4__sub-list {
- display: block;
- /* show sublist */
-}
-
-/* separator */
-.sidenav-v4__separator span {
- display: block;
- width: var(--sidenav-v4-icon-size);
- height: 1px;
- background-color: var(--color-contrast-lower);
-}
-
-/* mobile only */
-@media not all and (min-width: 64rem) {
- .sidenav-v4__item--collapsed .sidenav-v4__sub-list {
- display: none;
- }
-
- .sidenav-v4__link--href {
- display: none;
- /* hide link -> show button */
- }
-}
-/* desktop */
-@media (min-width: 64rem) {
- .sidenav-v4__sub-list {
- display: none;
- }
-
- .sidenav-v4__link,
-.sidenav-v4__sub-link,
-.sidenav-v4__separator {
- padding: var(--space-xs);
- }
-
- .sidenav-v4__link,
-.sidenav-v4__sub-link {
- font-size: var(--text-sm);
- }
-
- .sidenav-v4__link--btn {
- display: none;
- /* hide button -> show link */
- }
-
- /* tooltip */
- .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-list {
- width: 220px;
- position: absolute;
- z-index: var(--z-index-overlay);
- left: 100%;
- top: 0;
- background-color: var(--color-bg-light);
- box-shadow: var(--inner-glow), var(--shadow-md);
- border-radius: var(--radius-md);
- overflow: hidden;
- }
- .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-link {
- border-radius: 0;
- color: var(--color-contrast-high);
- }
- .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-link::before {
- display: none;
- /* remove dot indicator */
- }
- .sidenav-v4__item:not(.sidenav-v4__item--current) .sidenav-v4__sub-link:hover {
- color: var(--color-primary);
- }
- .sidenav-v4__item:not(.sidenav-v4__item--current).sidenav-v4__item--hover .sidenav-v4__sub-list, .sidenav-v4__item:not(.sidenav-v4__item--current):focus-within .sidenav-v4__sub-list {
- display: block;
- }
- .sidenav-v4__item:not(.sidenav-v4__item--current):hover .sidenav-v4__link {
- /* highlight main link if tooltip is visible */
- color: var(--color-primary);
- background-color: hsla(var(--color-contrast-higher-h), var(--color-contrast-higher-s), var(--color-contrast-higher-l), 0.075);
- }
-
- /* notification marker */
- .sidenav-v4__notification-marker {
- display: block;
- }
-} \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html b/old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html
deleted file mode 100644
index 1131b4d..0000000
--- a/old-apps/projects/src/app/pages/nav/html/side-navigation-v4.html
+++ /dev/null
@@ -1,211 +0,0 @@
-<div class="padding-component hide@md no-js:is-hidden">
- <button class="btn btn--primary" aria-controls="sidenav-v4">Show sidebar</button>
-</div>
-
-<div class="flex@md">
- <aside id="sidenav-v4" class="sidebar sidebar--static@md js-sidebar" data-static-class="position-relative z-index-2 bg width-100% max-width-xxxxs shadow-sm">
- <div class="sidebar__panel">
- <!-- 👇 header visible only on mobile -->
- <header class="sidebar__header bg padding-y-sm padding-left-md padding-right-sm border-bottom z-index-2">
- <h1 class="text-md text-truncate" id="sidebar-title">Menu</h1>
-
- <button class="reset sidebar__close-btn js-sidebar__close-btn js-tab-focus">
- <svg class="icon icon--xs" viewBox="0 0 16 16"><title>Close panel</title><g stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10"><line x1="13.5" y1="2.5" x2="2.5" y2="13.5"></line><line x1="2.5" y1="2.5" x2="13.5" y2="13.5"></line></g></svg>
- </button>
- </header>
-
- <div class="position-relative z-index-1">
- <nav class="sidenav-v4 padding-xs js-sidenav-v4">
- <ul>
- <li class="sidenav-v4__item">
- <a class="sidenav-v4__link js-sidenav-v4__link" href="#0">
- <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20">
- <g fill="currentColor">
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 12v7H4v-7"></path>
- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 10l9-9 9 9"></path>
- <path d="M10 14a2 2 0 0 1 2 2v2H8v-2a2 2 0 0 1 2-2z"></path>
- </g>
- </svg>
-
- <span>Overview</span>
-
- <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20">
- <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round">
- <line x1="3" y1="3" x2="17" y2="17" />
- <line x1="17" y1="3" x2="3" y2="17" />
- </g>
- </svg>
- </a>
-
- <ul class="sidenav-v4__sub-list">
- <li>
- <a class="sidenav-v4__sub-link" href="#0">All Data</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">Category 1</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">Category 2</a>
- </li>
- </ul>
- </li>
-
- <li class="sidenav-v4__item sidenav-v4__item--current">
- <a class="sidenav-v4__link js-sidenav-v4__link" href="#0">
- <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20">
- <g fill="currentColor">
- <path d="M10 20a2 2 0 0 1-2-2h4a2 2 0 0 1-2 2z"></path>
- <path d="M19 15a3 3 0 0 1-3-3V7a6 6 0 0 0-6-6 6 6 0 0 0-6 6v5a3 3 0 0 1-3 3h18z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
- </g>
- </svg>
-
- <span>Notifications</span>
-
- <span class="sidenav-v4__notification-marker">8 <i class="sr-only">notifications</i></span>
-
- <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20">
- <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round">
- <line x1="3" y1="3" x2="17" y2="17" />
- <line x1="17" y1="3" x2="3" y2="17" />
- </g>
- </svg>
- </a>
-
- <ul class="sidenav-v4__sub-list">
- <li>
- <a class="sidenav-v4__sub-link" href="#0">All Notifications</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0" aria-current="page">Friends</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">Other</a>
- </li>
- </ul>
- </li>
-
- <li class="sidenav-v4__item">
- <a class="sidenav-v4__link js-sidenav-v4__link" href="#0">
- <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20">
- <g fill="currentColor">
- <path d="M17 2H3a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h4l3 4 3-4h4a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
- </g>
- </svg>
-
- <span>Comments</span>
-
- <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20">
- <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round">
- <line x1="3" y1="3" x2="17" y2="17" />
- <line x1="17" y1="3" x2="3" y2="17" />
- </g>
- </svg>
- </a>
-
- <ul class="sidenav-v4__sub-list">
- <li>
- <a class="sidenav-v4__sub-link" href="#0">All Comments</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">+ New Comment</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">Spam</a>
- </li>
- </ul>
- </li>
-
- <li class="sidenav-v4__separator" role="presentation"><span></span></li>
-
- <li class="sidenav-v4__item">
- <a class="sidenav-v4__link js-sidenav-v4__link" href="#0">
- <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20">
- <g fill="currentColor">
- <rect x="2" y="2" width="16" height="16" rx="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></rect>
- <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 14l6-6 2 6H6z"></path><circle cx="6.5" cy="6.5" r="1.5"></circle>
- </g>
- </svg>
-
- <span>Assets</span>
-
- <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20">
- <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round">
- <line x1="3" y1="3" x2="17" y2="17" />
- <line x1="17" y1="3" x2="3" y2="17" />
- </g>
- </svg>
- </a>
-
- <ul class="sidenav-v4__sub-list">
- <li>
- <a class="sidenav-v4__sub-link" href="#0">All Assets</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">Upload</a>
- </li>
- </ul>
- </li>
-
- <li class="sidenav-v4__item">
- <a class="sidenav-v4__link js-sidenav-v4__link" href="#0">
- <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20">
- <g fill="currentColor">
- <circle cx="10" cy="4" r="3" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></circle>
- <path d="M10 11a8 8 0 0 0-7.562 5.383A2 2 0 0 0 4.347 19h11.306a2 2 0 0 0 1.909-2.617A8 8 0 0 0 10 11z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
- </g>
- </svg>
-
- <span>Users</span>
-
- <svg class="sidenav-v4__arrow-icon icon margin-left-auto" viewBox="0 0 20 20">
- <g class="icon__group" fill="none" stroke="currentColor" stroke-width="2px" stroke-linecap="round" stroke-linejoin="round">
- <line x1="3" y1="3" x2="17" y2="17" />
- <line x1="17" y1="3" x2="3" y2="17" />
- </g>
- </svg>
- </a>
-
- <ul class="sidenav-v4__sub-list">
- <li>
- <a class="sidenav-v4__sub-link" href="#0">All Users</a>
- </li>
-
- <li>
- <a class="sidenav-v4__sub-link" href="#0">+ New User</a>
- </li>
- </ul>
- </li>
-
- <li class="sidenav-v4__item">
- <a class="sidenav-v4__link js-sidenav-v4__link" href="#0">
- <svg class="sidenav-v4__icon icon" viewBox="0 0 20 20">
- <g fill="currentColor">
- <path d="M11 16l-1.55 1.55a4.95 4.95 0 0 1-7 0 4.95 4.95 0 0 1 0-7l2.192-2.192a4.95 4.95 0 0 1 7 0A4.907 4.907 0 0 1 12.731 10" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
- <path d="M9 4l1.55-1.55a4.95 4.95 0 0 1 7 0 4.95 4.95 0 0 1 0 7l-2.192 2.192a4.95 4.95 0 0 1-7 0A4.907 4.907 0 0 1 7.269 10" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"></path>
- </g>
- </svg>
-
- <span>Link</span>
- </a>
- </li>
- </ul>
- </nav>
- </div>
- </div>
- </aside>
-
- <main class="position-relative z-index-1 flex-grow height-100vh sidebar-loaded:show">
- <!-- start main content -->
- <div class="text-component padding-md">
- <p>Main content.</p>
- </div>
- <!-- end main content -->
- </main>
-</div> \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/index.ts b/old-apps/projects/src/app/pages/nav/index.ts
deleted file mode 100644
index ca91c20..0000000
--- a/old-apps/projects/src/app/pages/nav/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import NavWrapper from "./nav-wrapper.svelte";
-import NavItem from "./nav-item.svelte";
-export {
- NavWrapper,
- NavItem
-} \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js b/old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js
deleted file mode 100644
index ed4a47d..0000000
--- a/old-apps/projects/src/app/pages/nav/js/_1_diagonal-movement.js
+++ /dev/null
@@ -1,296 +0,0 @@
-// File#: _1_diagonal-movement
-// Usage: codyhouse.co/license
-/*
- Modified version of the jQuery-menu-aim plugin
- https://github.com/kamens/jQuery-menu-aim
- - Replaced jQuery with Vanilla JS
- - Minor changes
-*/
-(function() {
- var menuAim = function(opts) {
- init(opts);
- };
-
- window.menuAim = menuAim;
-
- function init(opts) {
- var activeRow = null,
- mouseLocs = [],
- lastDelayLoc = null,
- timeoutId = null,
- options = Util.extend({
- menu: '',
- rows: false, //if false, get direct children - otherwise pass nodes list
- submenuSelector: "*",
- submenuDirection: "right",
- tolerance: 75, // bigger = more forgivey when entering submenu
- enter: function(){},
- exit: function(){},
- activate: function(){},
- deactivate: function(){},
- exitMenu: function(){}
- }, opts),
- menu = options.menu;
-
- var MOUSE_LOCS_TRACKED = 3, // number of past mouse locations to track
- DELAY = 300; // ms delay when user appears to be entering submenu
-
- /**
- * Keep track of the last few locations of the mouse.
- */
- var mouseMoveFallback = function(event) {
- (!window.requestAnimationFrame) ? mousemoveDocument(event) : window.requestAnimationFrame(function(){mousemoveDocument(event);});
- };
-
- var mousemoveDocument = function(e) {
- mouseLocs.push({x: e.pageX, y: e.pageY});
-
- if (mouseLocs.length > MOUSE_LOCS_TRACKED) {
- mouseLocs.shift();
- }
- };
-
- /**
- * Cancel possible row activations when leaving the menu entirely
- */
- var mouseleaveMenu = function() {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
-
- // If exitMenu is supplied and returns true, deactivate the
- // currently active row on menu exit.
- if (options.exitMenu(this)) {
- if (activeRow) {
- options.deactivate(activeRow);
- }
-
- activeRow = null;
- }
- };
-
- /**
- * Trigger a possible row activation whenever entering a new row.
- */
- var mouseenterRow = function() {
- if (timeoutId) {
- // Cancel any previous activation delays
- clearTimeout(timeoutId);
- }
-
- options.enter(this);
- possiblyActivate(this);
- },
- mouseleaveRow = function() {
- options.exit(this);
- };
-
- /*
- * Immediately activate a row if the user clicks on it.
- */
- var clickRow = function() {
- activate(this);
- };
-
- /**
- * Activate a menu row.
- */
- var activate = function(row) {
- if (row == activeRow) {
- return;
- }
-
- if (activeRow) {
- options.deactivate(activeRow);
- }
-
- options.activate(row);
- activeRow = row;
- };
-
- /**
- * Possibly activate a menu row. If mouse movement indicates that we
- * shouldn't activate yet because user may be trying to enter
- * a submenu's content, then delay and check again later.
- */
- var possiblyActivate = function(row) {
- var delay = activationDelay();
-
- if (delay) {
- timeoutId = setTimeout(function() {
- possiblyActivate(row);
- }, delay);
- } else {
- activate(row);
- }
- };
-
- /**
- * Return the amount of time that should be used as a delay before the
- * currently hovered row is activated.
- *
- * Returns 0 if the activation should happen immediately. Otherwise,
- * returns the number of milliseconds that should be delayed before
- * checking again to see if the row should be activated.
- */
- var activationDelay = function() {
- if (!activeRow || !Util.is(activeRow, options.submenuSelector)) {
- // If there is no other submenu row already active, then
- // go ahead and activate immediately.
- return 0;
- }
-
- function getOffset(element) {
- var rect = element.getBoundingClientRect();
- return { top: rect.top + window.pageYOffset, left: rect.left + window.pageXOffset };
- };
-
- var offset = getOffset(menu),
- upperLeft = {
- x: offset.left,
- y: offset.top - options.tolerance
- },
- upperRight = {
- x: offset.left + menu.offsetWidth,
- y: upperLeft.y
- },
- lowerLeft = {
- x: offset.left,
- y: offset.top + menu.offsetHeight + options.tolerance
- },
- lowerRight = {
- x: offset.left + menu.offsetWidth,
- y: lowerLeft.y
- },
- loc = mouseLocs[mouseLocs.length - 1],
- prevLoc = mouseLocs[0];
-
- if (!loc) {
- return 0;
- }
-
- if (!prevLoc) {
- prevLoc = loc;
- }
-
- if (prevLoc.x < offset.left || prevLoc.x > lowerRight.x || prevLoc.y < offset.top || prevLoc.y > lowerRight.y) {
- // If the previous mouse location was outside of the entire
- // menu's bounds, immediately activate.
- return 0;
- }
-
- if (lastDelayLoc && loc.x == lastDelayLoc.x && loc.y == lastDelayLoc.y) {
- // If the mouse hasn't moved since the last time we checked
- // for activation status, immediately activate.
- return 0;
- }
-
- // Detect if the user is moving towards the currently activated
- // submenu.
- //
- // If the mouse is heading relatively clearly towards
- // the submenu's content, we should wait and give the user more
- // time before activating a new row. If the mouse is heading
- // elsewhere, we can immediately activate a new row.
- //
- // We detect this by calculating the slope formed between the
- // current mouse location and the upper/lower right points of
- // the menu. We do the same for the previous mouse location.
- // If the current mouse location's slopes are
- // increasing/decreasing appropriately compared to the
- // previous's, we know the user is moving toward the submenu.
- //
- // Note that since the y-axis increases as the cursor moves
- // down the screen, we are looking for the slope between the
- // cursor and the upper right corner to decrease over time, not
- // increase (somewhat counterintuitively).
- function slope(a, b) {
- return (b.y - a.y) / (b.x - a.x);
- };
-
- var decreasingCorner = upperRight,
- increasingCorner = lowerRight;
-
- // Our expectations for decreasing or increasing slope values
- // depends on which direction the submenu opens relative to the
- // main menu. By default, if the menu opens on the right, we
- // expect the slope between the cursor and the upper right
- // corner to decrease over time, as explained above. If the
- // submenu opens in a different direction, we change our slope
- // expectations.
- if (options.submenuDirection == "left") {
- decreasingCorner = lowerLeft;
- increasingCorner = upperLeft;
- } else if (options.submenuDirection == "below") {
- decreasingCorner = lowerRight;
- increasingCorner = lowerLeft;
- } else if (options.submenuDirection == "above") {
- decreasingCorner = upperLeft;
- increasingCorner = upperRight;
- }
-
- var decreasingSlope = slope(loc, decreasingCorner),
- increasingSlope = slope(loc, increasingCorner),
- prevDecreasingSlope = slope(prevLoc, decreasingCorner),
- prevIncreasingSlope = slope(prevLoc, increasingCorner);
-
- if (decreasingSlope < prevDecreasingSlope && increasingSlope > prevIncreasingSlope) {
- // Mouse is moving from previous location towards the
- // currently activated submenu. Delay before activating a
- // new menu row, because user may be moving into submenu.
- lastDelayLoc = loc;
- return DELAY;
- }
-
- lastDelayLoc = null;
- return 0;
- };
-
- var reset = function(triggerDeactivate) {
- if (timeoutId) {
- clearTimeout(timeoutId);
- }
-
- if (activeRow && triggerDeactivate) {
- options.deactivate(activeRow);
- }
-
- activeRow = null;
- };
-
- var destroyInstance = function() {
- menu.removeEventListener('mouseleave', mouseleaveMenu);
- document.removeEventListener('mousemove', mouseMoveFallback);
- if(rows.length > 0) {
- for(var i = 0; i < rows.length; i++) {
- rows[i].removeEventListener('mouseenter', mouseenterRow);
- rows[i].removeEventListener('mouseleave', mouseleaveRow);
- rows[i].removeEventListener('click', clickRow);
- }
- }
-
- };
-
- /**
- * Hook up initial menu events
- */
- menu.addEventListener('mouseleave', mouseleaveMenu);
- var rows = (options.rows) ? options.rows : menu.children;
- if(rows.length > 0) {
- for(var i = 0; i < rows.length; i++) {(function(i){
- rows[i].addEventListener('mouseenter', mouseenterRow);
- rows[i].addEventListener('mouseleave', mouseleaveRow);
- rows[i].addEventListener('click', clickRow);
- })(i);}
- }
-
- document.addEventListener('mousemove', mouseMoveFallback);
-
- /* Reset/destroy menu */
- menu.addEventListener('reset', function(event){
- reset(event.detail);
- });
- menu.addEventListener('destroy', destroyInstance);
- };
-}());
-
diff --git a/old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js b/old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js
deleted file mode 100644
index f9599d8..0000000
--- a/old-apps/projects/src/app/pages/nav/js/_1_responsive-sidebar.js
+++ /dev/null
@@ -1,215 +0,0 @@
-// File#: _1_responsive-sidebar
-// Usage: codyhouse.co/license
-(function() {
- var Sidebar = function(element) {
- this.element = element;
- this.triggers = document.querySelectorAll('[aria-controls="'+this.element.getAttribute('id')+'"]');
- this.firstFocusable = null;
- this.lastFocusable = null;
- this.selectedTrigger = null;
- this.showClass = "sidebar--is-visible";
- this.staticClass = "sidebar--static";
- this.customStaticClass = "";
- this.readyClass = "sidebar--loaded";
- this.contentReadyClass = "sidebar-loaded:show";
- this.layout = false; // this will be static or mobile
- this.preventScrollEl = getPreventScrollEl(this);
- getCustomStaticClass(this); // custom classes for static version
- initSidebar(this);
- };
-
- function getPreventScrollEl(element) {
- var scrollEl = false;
- var querySelector = element.element.getAttribute('data-sidebar-prevent-scroll');
- if(querySelector) scrollEl = document.querySelector(querySelector);
- return scrollEl;
- };
-
- function getCustomStaticClass(element) {
- var customClasses = element.element.getAttribute('data-static-class');
- if(customClasses) element.customStaticClass = ' '+customClasses;
- };
-
- function initSidebar(sidebar) {
- initSidebarResize(sidebar); // handle changes in layout -> mobile to static and viceversa
-
- if ( sidebar.triggers ) { // open sidebar when clicking on trigger buttons - mobile layout only
- for(var i = 0; i < sidebar.triggers.length; i++) {
- sidebar.triggers[i].addEventListener('click', function(event) {
- event.preventDefault();
- toggleSidebar(sidebar, event.target);
- });
- }
- }
-
- // use the 'openSidebar' event to trigger the sidebar
- sidebar.element.addEventListener('openSidebar', function(event) {
- toggleSidebar(sidebar, event.detail);
- });
- };
-
- function toggleSidebar(sidebar, target) {
- if(Util.hasClass(sidebar.element, sidebar.showClass)) {
- sidebar.selectedTrigger = target;
- closeSidebar(sidebar);
- return;
- }
- sidebar.selectedTrigger = target;
- showSidebar(sidebar);
- initSidebarEvents(sidebar);
- };
-
- function showSidebar(sidebar) { // mobile layout only
- Util.addClass(sidebar.element, sidebar.showClass);
- getFocusableElements(sidebar);
- Util.moveFocus(sidebar.element);
- // change the overflow of the preventScrollEl
- if(sidebar.preventScrollEl) sidebar.preventScrollEl.style.overflow = 'hidden';
- };
-
- function closeSidebar(sidebar) { // mobile layout only
- Util.removeClass(sidebar.element, sidebar.showClass);
- sidebar.firstFocusable = null;
- sidebar.lastFocusable = null;
- if(sidebar.selectedTrigger) sidebar.selectedTrigger.focus();
- sidebar.element.removeAttribute('tabindex');
- //remove listeners
- cancelSidebarEvents(sidebar);
- // change the overflow of the preventScrollEl
- if(sidebar.preventScrollEl) sidebar.preventScrollEl.style.overflow = '';
- };
-
- function initSidebarEvents(sidebar) { // mobile layout only
- //add event listeners
- sidebar.element.addEventListener('keydown', handleEvent.bind(sidebar));
- sidebar.element.addEventListener('click', handleEvent.bind(sidebar));
- };
-
- function cancelSidebarEvents(sidebar) { // mobile layout only
- //remove event listeners
- sidebar.element.removeEventListener('keydown', handleEvent.bind(sidebar));
- sidebar.element.removeEventListener('click', handleEvent.bind(sidebar));
- };
-
- function handleEvent(event) { // mobile layout only
- switch(event.type) {
- case 'click': {
- initClick(this, event);
- }
- case 'keydown': {
- initKeyDown(this, event);
- }
- }
- };
-
- function initKeyDown(sidebar, event) { // mobile layout only
- if( event.keyCode && event.keyCode == 27 || event.key && event.key == 'Escape' ) {
- //close sidebar window on esc
- closeSidebar(sidebar);
- } else if( event.keyCode && event.keyCode == 9 || event.key && event.key == 'Tab' ) {
- //trap focus inside sidebar
- trapFocus(sidebar, event);
- }
- };
-
- function initClick(sidebar, event) { // mobile layout only
- //close sidebar when clicking on close button or sidebar bg layer
- if( !event.target.closest('.js-sidebar__close-btn') && !Util.hasClass(event.target, 'js-sidebar') ) return;
- event.preventDefault();
- closeSidebar(sidebar);
- };
-
- function trapFocus(sidebar, event) { // mobile layout only
- if( sidebar.firstFocusable == document.activeElement && event.shiftKey) {
- //on Shift+Tab -> focus last focusable element when focus moves out of sidebar
- event.preventDefault();
- sidebar.lastFocusable.focus();
- }
- if( sidebar.lastFocusable == document.activeElement && !event.shiftKey) {
- //on Tab -> focus first focusable element when focus moves out of sidebar
- event.preventDefault();
- sidebar.firstFocusable.focus();
- }
- };
-
- function initSidebarResize(sidebar) {
- // custom event emitted when window is resized - detect only if the sidebar--static@{breakpoint} class was added
- var beforeContent = getComputedStyle(sidebar.element, ':before').getPropertyValue('content');
- if(beforeContent && beforeContent !='' && beforeContent !='none') {
- checkSidebarLayout(sidebar);
-
- sidebar.element.addEventListener('update-sidebar', function(event){
- checkSidebarLayout(sidebar);
- });
- }
- // check if there a main element to show
- var mainContent = document.getElementsByClassName(sidebar.contentReadyClass);
- if(mainContent.length > 0) Util.removeClass(mainContent[0], sidebar.contentReadyClass);
- Util.addClass(sidebar.element, sidebar.readyClass);
- };
-
- function checkSidebarLayout(sidebar) {
- var layout = getComputedStyle(sidebar.element, ':before').getPropertyValue('content').replace(/\'|"/g, '');
- if(layout == sidebar.layout) return;
- sidebar.layout = layout;
- if(layout != 'static') Util.addClass(sidebar.element, 'is-hidden');
- Util.toggleClass(sidebar.element, sidebar.staticClass + sidebar.customStaticClass, layout == 'static');
- if(layout != 'static') setTimeout(function(){Util.removeClass(sidebar.element, 'is-hidden')});
- // reset element role
- (layout == 'static') ? sidebar.element.removeAttribute('role', 'alertdialog') : sidebar.element.setAttribute('role', 'alertdialog');
- // reset mobile behaviour
- if(layout == 'static' && Util.hasClass(sidebar.element, sidebar.showClass)) closeSidebar(sidebar);
- };
-
- function getFocusableElements(sidebar) {
- //get all focusable elements inside the drawer
- var allFocusable = sidebar.element.querySelectorAll('[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex]:not([tabindex="-1"]), [contenteditable], audio[controls], video[controls], summary');
- getFirstVisible(sidebar, allFocusable);
- getLastVisible(sidebar, allFocusable);
- };
-
- function getFirstVisible(sidebar, elements) {
- //get first visible focusable element inside the sidebar
- for(var i = 0; i < elements.length; i++) {
- if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) {
- sidebar.firstFocusable = elements[i];
- return true;
- }
- }
- };
-
- function getLastVisible(sidebar, elements) {
- //get last visible focusable element inside the sidebar
- for(var i = elements.length - 1; i >= 0; i--) {
- if( elements[i].offsetWidth || elements[i].offsetHeight || elements[i].getClientRects().length ) {
- sidebar.lastFocusable = elements[i];
- return true;
- }
- }
- };
-
- window.Sidebar = Sidebar;
-
- //initialize the Sidebar objects
- var sidebar = document.getElementsByClassName('js-sidebar');
- if( sidebar.length > 0 ) {
- for( var i = 0; i < sidebar.length; i++) {
- (function(i){new Sidebar(sidebar[i]);})(i);
- }
- // switch from mobile to static layout
- var customEvent = new CustomEvent('update-sidebar');
- window.addEventListener('resize', function(event){
- (!window.requestAnimationFrame) ? setTimeout(function(){resetLayout();}, 250) : window.requestAnimationFrame(resetLayout);
- });
-
- (window.requestAnimationFrame) // init sidebar layout
- ? window.requestAnimationFrame(resetLayout)
- : resetLayout();
-
- function resetLayout() {
- for( var i = 0; i < sidebar.length; i++) {
- (function(i){sidebar[i].dispatchEvent(customEvent)})(i);
- };
- };
- }
-}()); \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js b/old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js
deleted file mode 100644
index 63ef9c4..0000000
--- a/old-apps/projects/src/app/pages/nav/js/_2_side-navigation-v4.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// File#: _2_side-navigation-v4
-// Usage: codyhouse.co/license
-(function() {
- function initSideNav(nav) {
- // create btns - visible on mobile only
- createBtns(nav);
- // toggle sublists on mobile when clicking on buttons
- toggleSubLists(nav);
- // init diagonal movement
- initDiagonalMove(nav);
- };
-
- function createBtns(nav) {
- // on mobile -> create a <button> element for each link with a submenu
- var expandableLinks = nav.getElementsByClassName('js-sidenav-v4__link');
- for(var i = 0; i < expandableLinks.length; i++) {
- createSingleBtn(expandableLinks[i]);
- }
- };
-
- function createSingleBtn(link) {
- if(!hasSubList(link)) return;
- // create btn and insert it into the DOM
- var btnClasses = link.getAttribute('class').replace('js-sidenav-v4__link', 'js-sidenav-v4__btn');
- btnClasses = btnClasses +' sidenav-v4__link--btn';
- var btnHtml = '<button class="reset '+btnClasses+'">'+link.innerHTML+'</button>';
- link.insertAdjacentHTML('afterend', btnHtml);
- // add class to link element
- Util.addClass(link, 'sidenav-v4__link--href');
- // check if we need to add the collpsed class to the <li> element
- var listItem = link.parentElement;
- if(!Util.hasClass(listItem, 'sidenav-v4__item--current')) Util.addClass(listItem, 'sidenav-v4__item--collapsed');
- };
-
- function hasSubList(link) {
- // check if link has submenu
- var sublist = link.nextElementSibling;
- if(!sublist) return false;
- return Util.hasClass(sublist, 'sidenav-v4__sub-list');
- };
-
- function toggleSubLists(nav) {
- // open/close sublist on mobile
- nav.addEventListener('click', function(event){
- var btn = event.target.closest('.js-sidenav-v4__btn');
- if(!btn) return;
- Util.toggleClass(btn.parentElement, 'sidenav-v4__item--collapsed', !Util.hasClass(btn.parentElement, 'sidenav-v4__item--collapsed'));
- });
- };
-
- function initDiagonalMove(nav) {
- // improve dropdown navigation
- new menuAim({
- menu: nav.querySelector('ul'),
- activate: function(row) {
- Util.addClass(row, 'sidenav-v4__item--hover');
- },
- deactivate: function(row) {
- Util.removeClass(row, 'sidenav-v4__item--hover');
- },
- exitMenu: function() {
- return true;
- },
- });
- };
-
- var sideNavs = document.getElementsByClassName('js-sidenav-v4');
- if( sideNavs.length > 0 ) {
- for( var i = 0; i < sideNavs.length; i++) {
- (function(i){initSideNav(sideNavs[i]);})(i);
- }
- }
-}()); \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/nav-item.svelte b/old-apps/projects/src/app/pages/nav/nav-item.svelte
deleted file mode 100644
index 335cbbb..0000000
--- a/old-apps/projects/src/app/pages/nav/nav-item.svelte
+++ /dev/null
@@ -1,18 +0,0 @@
-<script lang="ts">
- import {link} from "svelte-spa-router";
- import Icon from "$shared/components/icon.svelte";
-
- export let external = "";
- export let to = "";
- export let text;
- export let icon;
-</script>
-
-<li class="sidenav-v4__item">
- <a class="sidenav-v4__link" href={to ?? external} use:link={external === ""}>
- {#if icon}
- <Icon class="sidenav-v4__icon icon" name="{icon}" />
- {/if}
- <span>{text}</span>
- </a>
-</li>
diff --git a/old-apps/projects/src/app/pages/nav/nav-wrapper.svelte b/old-apps/projects/src/app/pages/nav/nav-wrapper.svelte
deleted file mode 100644
index 8321544..0000000
--- a/old-apps/projects/src/app/pages/nav/nav-wrapper.svelte
+++ /dev/null
@@ -1,20 +0,0 @@
-<script lang="ts">
- import {random_string} from "$shared/lib/helpers";
-
- export let id = "nav__" + random_string(4);
- const staticClasses = "position-relative z-index-2 bg width-100% max-width-xxxxs shadow-sm"
-</script>
-<div class="flex@md">
- <aside id="{id}" class="sidebar sidebar--static@md {staticClasses}">
- <div class="sidebar__panel">
- <div class="position-relative z-index-1">
- <nav class="sidenav-v4 padding-xs">
- <slot name="navigation-items"></slot>
- </nav>
- </div>
- </div>
- </aside>
- <main class="container max-width-xl position-relative z-index-1 flex-grow min-height-100vh position-sticky@md top-0@md height-100vh@md overflow-auto@m">
- <slot name="main-content"></slot>
- </main>
-</div> \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss b/old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss
deleted file mode 100644
index e4304f1..0000000
--- a/old-apps/projects/src/app/pages/nav/scss/_1_responsive-sidebar.scss
+++ /dev/null
@@ -1,147 +0,0 @@
-@use '../base' as *;
-
-/* --------------------------------
-
-File#: _1_responsive-sidebar
-Title: Responsive Sidebar
-Descr: Responsive sidebar container
-Usage: codyhouse.co/license
-
--------------------------------- */
-
-/* mobile version only (--default) 👇 */
-.sidebar:not(.sidebar--static) {
- position: fixed;
- top: 0;
- left: 0;
- z-index: var(--z-index-fixed-element, 10);
- width: 100%;
- height: 100%;
- visibility: hidden;
- transition: visibility 0s 0.3s;
-
- &::after { /* overlay layer */
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: alpha(var(--color-black), 0);
- transition: background-color .3s;
- z-index: 1;
- }
-
- .sidebar__panel { /* content */
- position: absolute;
- top: 0;
- left: 0;
- z-index: 2;
- width: 100%;
- max-width: 380px;
- height: 100%;
- overflow: auto;
- -webkit-overflow-scrolling: touch;
- background-color: var(--color-bg);
- transform: translateX(-100%);
- transition: box-shadow 0.3s,transform 0.3s;
- }
-
- &.sidebar--right-on-mobile {
- .sidebar__panel {
- left: auto;
- right: 0;
- transform: translateX(100%);
- }
- }
-
- &.sidebar--is-visible {
- visibility: visible;
- transition: none;
-
- &::after {
- background-color: alpha(var(--color-black), 0.85);
- }
-
- .sidebar__panel {
- transform: translateX(0);
- box-shadow: var(--shadow-md);
- }
- }
-}
-/* end mobile version */
-
-.sidebar__header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- position: sticky;
- top: 0;
-}
-
-.sidebar__close-btn {
- --size: 32px;
- width: var(--size);
- height: var(--size);
- display: flex;
- border-radius: 50%;
- background-color: var(--color-bg-light);
- box-shadow: var(--inner-glow), var(--shadow-sm);
- transition: .2s;
- flex-shrink: 0;
-
- .icon {
- display: block;
- margin: auto;
- }
-
- &:hover {
- background-color: var(--color-bg-lighter);
- box-shadow: var(--inner-glow), var(--shadow-md);
- }
-}
-
-/* desktop version only (--static) 👇 */
-.sidebar--static {
- flex-shrink: 0;
- flex-grow: 1;
-
- .sidebar__header {
- display: none;
- }
-}
-
-.sidebar--sticky-on-desktop {
- position: sticky;
- top: var(--space-sm);
- max-height: calc(100vh - var(--space-sm));
- overflow: auto;
- -webkit-overflow-scrolling: touch;
-}
-/* end desktop version */
-
-.sidebar, .sidebar-loaded\:show {
- opacity: 0; /* hide sidebar - or other elements using the .sidebar-loaded:show class - while it is initialized in JS */
-}
-
-.sidebar--loaded {
- opacity: 1;
-}
-
-/* detect when the sidebar needs to switch from the mobile layout to a static one - used in JS */
-[class*="sidebar--static"]::before {
- display: none;
-}
-
-.sidebar--static::before {
- content: 'static';
-}
-
-@each $breakpoint, $value in $breakpoints {
- .sidebar--static\@#{$breakpoint}::before {
- content: 'mobile';
- @include breakpoint(#{$breakpoint}) {
- content: 'static';
- }
- }
-} \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss b/old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss
deleted file mode 100644
index 2b421df..0000000
--- a/old-apps/projects/src/app/pages/nav/scss/_2_side-navigation-v4.scss
+++ /dev/null
@@ -1,237 +0,0 @@
-@use '../base' as *;
-@use '_1_responsive-sidebar.scss' as *;
-
-/* --------------------------------
-
-File#: _2_side-navigation-v4
-Title: Side Navigation v4
-Descr: Main, side navigation
-Usage: codyhouse.co/license
-
--------------------------------- */
-
-.sidenav-v4 {
- --sidenav-v4-icon-size: 20px;
- --sidenav-v4-icon-margin-right: var(--space-xxs);
-}
-
-.sidenav-v4__item {
- position: relative;
-}
-
-.sidenav-v4__link,
-.sidenav-v4__sub-link,
-.sidenav-v4__separator {
- padding: var(--space-sm);
-}
-
-.sidenav-v4__link, .sidenav-v4__sub-link {
- display: flex;
- align-items: center;
-
- width: 100%;
- border-radius: var(--radius-md);
-
- text-decoration: none;
- color: inherit;
- line-height: 1;
- font-size: var(--text-md);
-
- transition: .2s;
-
- &:hover {
- color: var(--color-primary);
- background-color: alpha(var(--color-contrast-higher), 0.075);
- }
-
- &[aria-current="page"] {
- color: var(--color-primary);
- }
-}
-
-.sidenav-v4__sub-link {
- position: relative;
- color: var(--color-contrast-medium);
-
- /* dot indicator */
- &::before {
- content: '';
- display: block;
- --size: 6px;
- width: var(--size);
- height: var(--size);
- background: currentColor;
- border-radius: 50%;
- margin-left: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2);
- margin-right: calc(var(--sidenav-v4-icon-size)/2 - var(--size)/2 + var(--sidenav-v4-icon-margin-right));
-
- opacity: 0; /* visible only if current */
- }
-
- &[aria-current="page"] {
- &::before { /* show dot indicator */
- opacity: 1;
- }
- }
-}
-
-.sidenav-v4__notification-marker {
- margin-left: auto;
- background-color: var(--color-accent);
- border-radius: var(--radius-md);
-
- height: 16px;
- line-height: 16px;
- padding: 0 4px;
- color: var(--color-white);
- font-size: 12px;
-
- /* hide - visible only on desktop */
- display: none;
-}
-
-/* label icon */
-.sidenav-v4__icon {
- --size: var(--sidenav-v4-icon-size);
- margin-right: var(--sidenav-v4-icon-margin-right);
-}
-
-/* arrow icon - visible on mobile if item is expandable */
-.sidenav-v4__arrow-icon {
- --size: 20px;
-
- .icon__group {
- will-change: transform;
- transform-origin: 50% 50%;
- transform: rotate(-90deg);
- transition: transform .3s var(--ease-out);
-
- > * {
- transform-origin: 50% 50%;
- stroke-dasharray: 20;
- stroke-dashoffset: 0;
- transform: translateY(0px);
- transition: transform .3s, stroke-dashoffset .3s;
- transition-timing-function: var(--ease-out);
- }
-
- .sidenav-v4__item--collapsed & {
- transform: rotate(0deg);
-
- > * {
- transform: translateY(4px);
- }
-
- > *:first-child {
- stroke-dashoffset: 10.15;
- }
-
- > *:last-child {
- stroke-dashoffset: 10.15;
- }
- }
- }
-
- /* hide icon for links - show only for buttons created in JS */
- .sidenav-v4__link--href & {
- display: none;
- }
-}
-
-/* current item */
-.sidenav-v4__item--current {
- .sidenav-v4__sub-list {
- display: block; /* show sublist */
- }
-}
-
-/* separator */
-.sidenav-v4__separator {
- span {
- display: block;
- width: var(--sidenav-v4-icon-size);
- height: 1px;
- background-color: var(--color-contrast-lower);
- }
-}
-
-/* mobile only */
-@include breakpoint(md, "not all") {
- .sidenav-v4__item--collapsed {
- .sidenav-v4__sub-list {
- display: none;
- }
- }
-
- .sidenav-v4__link--href {
- display: none; /* hide link -> show button */
- }
-}
-
-/* desktop */
-@include breakpoint(md) {
- .sidenav-v4__sub-list {
- display: none;
- }
-
- .sidenav-v4__link,
- .sidenav-v4__sub-link,
- .sidenav-v4__separator {
- padding: var(--space-xs);
- }
-
- .sidenav-v4__link,
- .sidenav-v4__sub-link {
- font-size: var(--text-sm);
- }
-
- .sidenav-v4__link--btn {
- display: none; /* hide button -> show link */
- }
-
- /* tooltip */
- .sidenav-v4__item:not(.sidenav-v4__item--current) {
- .sidenav-v4__sub-list {
- width: 220px;
- position: absolute;
- z-index: var(--z-index-overlay);
- left: 100%;
- top: 0;
-
- background-color: var(--color-bg-light);
- box-shadow: var(--inner-glow), var(--shadow-md);
- border-radius: var(--radius-md);
-
- overflow: hidden;
- }
-
- .sidenav-v4__sub-link {
- border-radius: 0;
- color: var(--color-contrast-high);
-
- &::before {
- display: none; /* remove dot indicator */
- }
-
- &:hover {
- color: var(--color-primary);
- }
- }
-
- &.sidenav-v4__item--hover, &:focus-within {
- .sidenav-v4__sub-list {
- display: block;
- }
- }
-
- &:hover .sidenav-v4__link { /* highlight main link if tooltip is visible */
- color: var(--color-primary);
- background-color: alpha(var(--color-contrast-higher), 0.075);
- }
- }
-
- /* notification marker */
- .sidenav-v4__notification-marker {
- display: block;
- }
-} \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/nav/side-navigation-v4.zip b/old-apps/projects/src/app/pages/nav/side-navigation-v4.zip
deleted file mode 100644
index d034eaf..0000000
--- a/old-apps/projects/src/app/pages/nav/side-navigation-v4.zip
+++ /dev/null
Binary files differ
diff --git a/old-apps/projects/src/app/pages/not-found.svelte b/old-apps/projects/src/app/pages/not-found.svelte
deleted file mode 100644
index 8822e0e..0000000
--- a/old-apps/projects/src/app/pages/not-found.svelte
+++ /dev/null
@@ -1,25 +0,0 @@
-<script>
- import LL from "$app/lib/i18n/i18n-svelte";
- import {link} from "svelte-spa-router";
-</script>
-
-<style>
- header {
- font-size: 12rem;
- }
-
- main {
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- text-align: center;
- }
-</style>
-
-<main>
- <header>404</header>
- <p>{$LL.messages.pageNotFound()}</p>
- <a use:link
- href="/">{$LL.messages.goToFrontpage()}</a>
-</main>
diff --git a/old-apps/projects/src/app/pages/settings.svelte b/old-apps/projects/src/app/pages/settings.svelte
deleted file mode 100644
index ca9fd47..0000000
--- a/old-apps/projects/src/app/pages/settings.svelte
+++ /dev/null
@@ -1,12 +0,0 @@
-<script>
- import Layout from "./_layout.svelte";
- import CategoriesTile from "$app/pages/views/settings-categories-tile.svelte";
- import LabelsTile from "$app/pages/views/settings-labels-tile.svelte";
-</script>
-
-<Layout>
- <section class="grid gap-md">
- <CategoriesTile/>
- <LabelsTile/>
- </section>
-</Layout>
diff --git a/old-apps/projects/src/app/pages/ui-workbench.svelte b/old-apps/projects/src/app/pages/ui-workbench.svelte
deleted file mode 100644
index ff2b058..0000000
--- a/old-apps/projects/src/app/pages/ui-workbench.svelte
+++ /dev/null
@@ -1,7 +0,0 @@
-<script>
- import {NavWrapper} from "./nav/index";
-</script>
-
-<NavWrapper>
-
-</NavWrapper> \ No newline at end of file
diff --git a/old-apps/projects/src/app/pages/views/category-form/index.svelte b/old-apps/projects/src/app/pages/views/category-form/index.svelte
deleted file mode 100644
index 21024c3..0000000
--- a/old-apps/projects/src/app/pages/views/category-form/index.svelte
+++ /dev/null
@@ -1,144 +0,0 @@
-<script lang="ts">
- import Alert from "$shared/components/alert.svelte";
- import Dropdown from "$shared/components/dropdown.svelte";
- import labels, {create_label_async} from "$app/lib/stores/labels";
- import {generate_random_hex_color} from "$shared/lib/colors";
- import LL from "$app/lib/i18n/i18n-svelte";
-
- let LabelsDropdown;
-
- const dough = {
- error: "",
- fields: {
- name: {
- value: "",
- error: "",
- validate() {
- return false;
- }
- },
- color: {
- value: "",
- error: "",
- validate() {
- return true;
- }
- },
- labels: {
- loading: false,
- value: [],
- error: "",
- validate() {
- return true;
- },
- async create({name}) {
- dough.fields.labels.loading = true;
- const response = await create_label_async({
- name: name,
- color: generate_random_hex_color(),
- });
- dough.fields.labels.loading = false;
- if (response.ok) {
- // Small pause to allow loading state to update everywhere.
- setTimeout(() => LabelsDropdown.select_entry(response.data.id), 50);
- }
- }
- },
- archived: {
- value: false,
- error: "",
- validate() {
- return true;
- }
- }
- },
- bake() {
- // labels.filter((c) => Object.hasOwn(c, "selected") && c.selected === true);
- return {
- labels: dough.fields.labels.value,
- name: dough.fields.name.value,
- color: dough.fields.color.value,
- };
- },
- submit(event) {
- const bread = dough.bake();
- console.log(bread);
- console.log("Submitted");
- }
- };
-
- const functions = {
- set(values) {
- functions.set_archived(values.archived);
- functions.set_labels(values.labels);
- functions.set_color(values.color);
- functions.set_name(values.name);
- },
- is_valid() {
- let isValid = true;
- if (!dough.fields.labels.validate()) isValid = false;
- if (!dough.fields.color.validate()) isValid = false;
- if (!dough.fields.name.validate()) isValid = false;
- if (!dough.fields.archived.validate()) isValid = false;
- return isValid;
- },
- set_archived(value) {
- dough.fields.archived.value = value;
- },
- set_labels(value) {
- dough.fields.labels.value = value;
- },
- set_color(value) {
- dough.fields.color.value = value;
- },
- set_name(value) {
- dough.fields.name.value = value;
- },
- };
-</script>
-
-<form on:submit|preventDefault={dough.submit}>
- <div class="margin-y-sm">
- <Alert visible={dough.error !== ""}
- message={dough.error}
- type="error"/>
- </div>
- <div class="grid gap-x-xs margin-bottom-sm">
- <div class="col-10">
- <label for="name"
- class="form-label margin-bottom-xxs">{$LL.views.categoryForm.name()}</label>
- <input type="text"
- class="form-control width-100%"
- id="name"
- bind:value={dough.fields.name.value}/>
- {#if dough.fields.name.error}
- <small class="color-error">{dough.fields.name.error}</small>
- {/if}
- </div>
- <div class="col-2">
- <label for="color"
- class="form-label margin-bottom-xxs">{$LL.views.categoryForm.color()}</label>
- <input type="color"
- class="form-control width-100%"
- id="color"
- style="height: 41px"
- bind:value={dough.fields.color.value}/>
- {#if dough.fields.color.error}
- <small class="color-error">{dough.fields.color.error}</small>
- {/if}
- </div>
- </div>
- <div class="margin-bottom-sm">
- <label for="labels"
- class="form-label margin-bottom-xxs">{$LL.views.categoryForm.defaultLabels()}</label>
- <Dropdown id="labels"
- createable={true}
- placeholder="{$LL.views.categoryForm.labelsPlaceholder()}"
- entries={$labels}
- multiple={true}
- on_create_async={(name) => dough.fields.labels.create({name})}/>
- {#if dough.fields.labels.error}
- <small class="color-error">{dough.fields.labels.error}</small>
- {/if}
- </div>
-</form>
diff --git a/old-apps/projects/src/app/pages/views/data-table-paginator.svelte b/old-apps/projects/src/app/pages/views/data-table-paginator.svelte
deleted file mode 100644
index b2649eb..0000000
--- a/old-apps/projects/src/app/pages/views/data-table-paginator.svelte
+++ /dev/null
@@ -1,101 +0,0 @@
-<script>
- import LL from "$app/lib/i18n/i18n-svelte";
- import {createEventDispatcher, onMount} from "svelte";
- import {restrict_input_to_numbers} from "$shared/lib/helpers";
-
- const dispatch = createEventDispatcher();
- export let page = 1;
- export let pageCount = 1;
- let prevCount = page;
- let canIncrement = false;
- let canDecrement = false;
- $: canIncrement = page < pageCount;
- $: canDecrement = page > 1;
-
- onMount(() => {
- restrict_input_to_numbers(document.querySelector("#curr-page"));
- });
-
- function increment() {
- if (canIncrement) {
- page++;
- }
- }
-
- function decrement() {
- if (canDecrement) {
- page--;
- }
- }
-
- $: if (page) {
- handle_change();
- }
-
- function handle_change() {
- if (page === prevCount) {
- return;
- }
- prevCount = page;
- if (page > pageCount) {
- page = pageCount;
- }
- dispatch("value_change", {
- newValue: page,
- });
- }
-</script>
-
-<nav class="pagination"
- aria-label="Pagination">
- <ul class="pagination__list flex flex-wrap gap-xxxs justify-center justify-end@md">
- <li>
- <button on:click={decrement}
- class="reset pagination__item {canDecrement ? '' : 'c-disabled'}">
- <svg class="icon icon--xs flip-x"
- viewBox="0 0 16 16">
- <title>{$LL.views.dataTablePaginator.goToPrevPage()}</title>
- <polyline
- points="6 2 12 8 6 14"
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- />
- </svg>
- </button>
- </li>
-
- <li>
- <span class="pagination__jumper flex items-center">
- <input aria-label="Page number"
- class="form-control"
- id="curr-page"
- type="text"
- on:change={handle_change}
- value={page}
- />
- <em>{$LL.views.dataTablePaginator.of()} {pageCount}</em>
- </span>
- </li>
-
- <li>
- <button on:click={increment}
- class="reset pagination__item {canIncrement ? '' : 'c-disabled'}">
- <svg class="icon icon--xs"
- viewBox="0 0 16 16">
- <title>{$LL.views.dataTablePaginator.goToNextPage()}</title>
- <polyline
- points="6 2 12 8 6 14"
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- />
- </svg>
- </button>
- </li>
- </ul>
-</nav>
diff --git a/old-apps/projects/src/app/pages/views/entry-form/index.svelte b/old-apps/projects/src/app/pages/views/entry-form/index.svelte
deleted file mode 100644
index e43d2a9..0000000
--- a/old-apps/projects/src/app/pages/views/entry-form/index.svelte
+++ /dev/null
@@ -1,199 +0,0 @@
-<script lang="ts">
- import LL from "$app/lib/i18n/i18n-svelte";
- import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto";
- import {Temporal} from "@js-temporal/polyfill";
- import {createEventDispatcher, onMount, onDestroy} from "svelte";
- import DateTimePart from "./sections/date-time.svelte";
- import LabelsPart from "./sections/labels.svelte";
- import CategoryPart from "./sections/category.svelte";
- import Button from "$shared/components/button.svelte";
- import {Textarea} from "$shared/components/form";
- import Alert from "$shared/components/alert.svelte";
- import {is_guid} from "$shared/lib/helpers";
- import {create_entry_async, edit_entry_async} from "$app/lib/stores/entries";
-
- const dispatch = createEventDispatcher();
-
- let formError = "";
- let formIsLoading = false;
- let isSubmitting = false;
- let description = "";
- let descriptionError = "";
- let dateTimePart;
- let labelsPart;
- let categoryPart;
- let entryId;
-
- onMount(() => {
- formIsLoading = true;
-
- Promise.all([categoryPart.load_categories(), labelsPart.load_labels()]).then(() => {
- formIsLoading = false;
- });
-
- window.addEventListener("keydown", handle_window_keydown);
- });
-
- onDestroy(() => {
- window.removeEventListener("keydown", handle_window_keydown);
- });
-
- function handle_window_keydown(event) {
- if (event.ctrlKey && event.code === "Enter") {
- submit_form();
- }
- }
-
- function validate_form() {
- return dateTimePart.is_valid() && categoryPart.is_valid() && description_is_valid();
- }
-
- function description_is_valid() {
- if (!description) {
- descriptionError = $LL.views.entryForm.errDescriptionReq();
- } else {
- descriptionError = "";
- }
-
- return description;
- }
-
- function get_payload() {
- const response = {} as TimeEntryDto;
- const values = get_values();
- if (!is_guid(values.id)) {
- delete values.id;
- } else {
- response.id = values.id;
- }
-
- const currentTimeZone = Temporal.Now.zonedDateTimeISO().offset;
- response.start = values.date + "T" + values.fromTimeValue + currentTimeZone.toString();
- response.stop = values.date + "T" + values.toTimeValue + currentTimeZone.toString();
-
- response.category = {
- id: values.category.id,
- };
-
- const selectedLabels = values.labels;
- if (selectedLabels?.length > 0 ?? false) {
- response.labels = selectedLabels;
- }
-
- const descriptionContent = description?.trim();
- if (descriptionContent?.length > 0 ?? false) {
- response.description = descriptionContent;
- }
-
- return response;
- }
-
- async function submit_form() {
- formError = "";
- if (validate_form()) {
- const payload = get_payload() as TimeEntryDto;
- isSubmitting = true;
- if (is_guid(payload.id)) {
- const response = await edit_entry_async(payload);
- if (response.ok) {
- functions.reset();
- dispatch("updated", response.data);
- } else {
- formError = $LL.views.entryForm.entryUpdateError();
- isSubmitting = false;
- }
- } else {
- const response = await create_entry_async(payload);
- if (response.ok) {
- functions.reset();
- dispatch("created");
- } else {
- formError = $LL.views.entryForm.entryCreateError();
- isSubmitting = false;
- }
- }
- }
- }
-
- function get_values() {
- return {
- id: entryId,
- toTimeValue: dateTimePart.get_to_time_value(),
- fromTimeValue: dateTimePart.get_from_time_value(),
- date: dateTimePart.get_date(),
- category: categoryPart.get_selected(),
- labels: labelsPart.get_selected(),
- description: description,
- };
- }
-
- export const functions = {
- set_values(values) {
- entryId = values.id;
- dateTimePart.set_values(values);
- labelsPart.select_labels(values?.labels.map((c) => c.id) ?? []);
- categoryPart.select_category(values?.category?.id);
- description = values.description;
- },
- set_time(value: {to: Temporal.PlainTime, from: Temporal.PlainTime}) {
- dateTimePart.set_times(value);
- },
- set_description(value: string) {
- if (description) description = description + "\n\n" + value;
- else description = value;
- },
- reset() {
- isSubmitting = false;
- formIsLoading = false;
- entryId = "";
- labelsPart.reset();
- categoryPart.reset();
- dateTimePart.reset(true);
- description = "";
- formError = "";
- },
- };
-</script>
-
-<form on:submit|preventDefault={submit_form}
- on:reset={() => functions.reset()}>
- <div class="margin-y-sm">
- <Alert visible={formError !== ""}
- message={formError}
- type="error"/>
- </div>
-
- <div class="margin-bottom-sm">
- <DateTimePart bind:functions={dateTimePart}/>
- </div>
-
- <div class="margin-bottom-sm">
- <CategoryPart bind:functions={categoryPart}/>
- </div>
-
- <div class="margin-bottom-sm">
- <LabelsPart bind:functions={labelsPart}/>
- </div>
-
- <div class="margin-bottom-sm">
- <Textarea class="width-100%"
- id="description"
- label="{$LL.views.entryForm.description()}"
- errorText="{descriptionError}"
- bind:value={description}></Textarea>
- </div>
-
- <div class="flex flex-row justify-end gap-x-xs">
- {#if entryId}
- <Button text="{$LL.views.entryForm.reset()}"
- on:click={() => functions.reset()}
- variant="subtle"
- />
- {/if}
- <Button loading={isSubmitting}
- type="submit"
- variant="primary"
- text={entryId ? $LL.views.entryForm.save() : $LL.views.entryForm.create()}
- />
- </div>
-</form>
diff --git a/old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte b/old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte
deleted file mode 100644
index f7af382..0000000
--- a/old-apps/projects/src/app/pages/views/entry-form/sections/category.svelte
+++ /dev/null
@@ -1,76 +0,0 @@
-<script>
- import {generate_random_hex_color} from "$shared/lib/colors";
- import Dropdown from "$shared/components/dropdown.svelte";
- import {is_guid, move_focus} from "$shared/lib/helpers";
- import categories, {reload_categories, create_category_async} from "$app/lib/stores/categories";
- import LL from "$app/lib/i18n/i18n-svelte"
-
- let categoriesError = "";
- let loading = false;
-
- let DropdownExports;
-
- function reset() {
- DropdownExports.reset();
- categoriesError = "";
- console.log($LL.views.entryForm.category._logReset());
- }
-
- async function on_create({name}) {
- loading = true;
- const response = await create_category_async({
- name: name,
- color: generate_random_hex_color(),
- });
- loading = false;
- if (response.ok) {
- // Small pause to allow loading state to update everywhere.
- setTimeout(() => select_category(response.data.id), 50);
- }
- }
-
- function get_selected() {
- return $categories.find((c) => c.selected === true);
- }
-
- function select_category(id) {
- DropdownExports.select(id);
- }
-
- function is_valid() {
- let isValid = true;
- const category = get_selected();
- if (!is_guid(category?.id)) {
- categoriesError = $LL.views.entryForm.category.errisRequired();
- isValid = false;
- move_focus(document.getElementById("category-dropdown"));
- } else {
- categoriesError = "";
- }
- return isValid;
- }
-
- export const functions = {
- get_selected,
- reset,
- is_valid,
- select_category,
- load_categories: reload_categories,
- };
-</script>
-
-<Dropdown
- entries={$categories}
- label="{$LL.views.entryForm.category.category()}"
- maxlength="50"
- createable={true}
- placeholder="{$LL.views.entryForm.category.placeholder()}"
- id="category-dropdown"
- loading={loading}
- name="category-dropdown"
- on_create_async={on_create}
- noResultsText="{$LL.views.entryForm.category.noResults()}"
- errorText="{categoriesError}"
- bind:this={DropdownExports}
-/>
-
diff --git a/old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte b/old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte
deleted file mode 100644
index b91f1a4..0000000
--- a/old-apps/projects/src/app/pages/views/entry-form/sections/date-time.svelte
+++ /dev/null
@@ -1,167 +0,0 @@
-<script lang="ts">
- import LL from "$app/lib/i18n/i18n-svelte";
- import { Temporal } from "@js-temporal/polyfill";
-
- // TIME
- let fromTimeValue = "";
- let fromTimeError = "";
- let toTimeValue = "";
- let toTimeError = "";
-
- function handle_from_time_changed(e) {
- fromTimeValue = e.target.value;
- if (fromTimeValue) {
- fromTimeError = "";
- }
- }
-
- function handle_to_time_changed(e) {
- toTimeValue = e.target.value;
- if (toTimeValue) {
- toTimeError = "";
- }
- }
-
- // DATE
- let date = Temporal.Now.plainDateTimeISO().toString().substring(0, 10);
- let dateError = "";
-
- function is_valid() {
- let isValid = true;
- let focusIsSet = false;
- if (!date) {
- dateError = $LL.views.entryForm.dateTime.errDateIsRequired();
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("date")?.focus();
- focusIsSet = true;
- }
- } else {
- dateError = "";
- }
-
- if (!fromTimeValue) {
- fromTimeError = $LL.views.entryForm.dateTime.errFromIsRequired();
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("from")?.focus();
- focusIsSet = true;
- }
- } else if (toTimeValue && fromTimeValue > toTimeValue) {
- fromTimeError = $LL.views.entryForm.dateTime.errFromAfterTo();
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("from")?.focus();
- focusIsSet = true;
- }
- } else if (fromTimeValue === toTimeValue) {
- fromTimeError = $LL.views.entryForm.dateTime.errFromEqTo();
-
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("from")?.focus();
- focusIsSet = true;
- }
- } else {
- fromTimeError = "";
- }
-
- if (!toTimeValue) {
- toTimeError = $LL.views.entryForm.dateTime.errToIsRequired();
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("to")?.focus();
- focusIsSet = true;
- }
- } else if (fromTimeValue && toTimeValue < fromTimeValue) {
- toTimeError = $LL.views.entryForm.dateTime.errToBeforeFrom();
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("to")?.focus();
- focusIsSet = true;
- }
- } else {
- toTimeError = "";
- }
-
- return isValid;
- }
-
- export const functions = {
- get_from_time_value() {
- return fromTimeValue;
- },
- get_to_time_value() {
- return toTimeValue;
- },
- get_date() {
- return date;
- },
- is_valid,
- reset(focusDate = false) {
- fromTimeValue = "";
- toTimeValue = "";
- if (focusDate) {
- document.getElementById("date")?.focus();
- }
- console.log($LL.views.entryForm.dateTime._logReset());
- },
- set_times(value) {
- fromTimeValue = value.from.toPlainTime().toString().substring(0, 5);
- toTimeValue = value.to.toPlainTime().toString().substring(0, 5);
- },
- set_date(new_date: Temporal.PlainDate) {
- date = new_date.toString();
- },
- set_values(values) {
- const currentTimeZone = Temporal.Now.timeZone().id;
- const startDate = Temporal.Instant.from(values.start);
- const stopDate = Temporal.Instant.from(values.stop);
- fromTimeValue = startDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5);
- toTimeValue = stopDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5);
- date = startDate.toZonedDateTimeISO(currentTimeZone).toPlainDate().toString();
- }
- };
-</script>
-
-<div class="grid gap-xs">
- <div class="col-4">
- <label for="date"
- class="form-label margin-bottom-xxs">{$LL.views.entryForm.dateTime.date()}</label>
- <input type="date"
- id="date"
- class="form-control width-100%"
- bind:value={date}>
- {#if dateError}
- <small class="color-error">{dateError}</small>
- {/if}
- </div>
- <div class="col-4">
- <label for="from"
- class="form-label margin-bottom-xxs">{$LL.views.entryForm.dateTime.from()}</label>
- <input id="from"
- class="form-control width-100%"
- pattern="[0-9][0-9]:[0-9][0-9]"
- type="time"
- bind:value={fromTimeValue}
- on:input={handle_from_time_changed}
- />
- {#if fromTimeError}
- <small class="color-error">{fromTimeError}</small>
- {/if}
- </div>
- <div class="col-4">
- <label for="to"
- class="form-label margin-bottom-xxs">{$LL.views.entryForm.dateTime.to()}</label>
- <input id="to"
- class="form-control width-100%"
- pattern="[0-9][0-9]:[0-9][0-9]"
- type="time"
- bind:value={toTimeValue}
- on:input={handle_to_time_changed}
- />
- {#if toTimeError}
- <small class="color-error">{toTimeError}</small>
- {/if}
- </div>
-</div>
diff --git a/old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte b/old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte
deleted file mode 100644
index a6f324b..0000000
--- a/old-apps/projects/src/app/pages/views/entry-form/sections/labels.svelte
+++ /dev/null
@@ -1,66 +0,0 @@
-<script>
- import LL from "$app/lib/i18n/i18n-svelte";
- import {generate_random_hex_color} from "$shared/lib/colors";
- import labels, {reload_labels, create_label_async} from "$app/lib/stores/labels";
- import Dropdown from "$shared/components/dropdown.svelte";
-
- let labelsError = "";
- let loading = false;
- let DropdownExports;
-
- function reset() {
- DropdownExports.reset();
- console.log($LL.views.entryForm.labels._logReset());
- }
-
- function get_selected() {
- return $labels.filter((c) => Object.hasOwn(c, "selected") && c.selected === true);
- }
-
- function select_label(id) {
- DropdownExports.select(id);
- }
-
- function select_labels(ids) {
- for (const id of ids) {
- DropdownExports.select(id);
- }
- }
-
- async function on_create({name}) {
- loading = true;
- const response = await create_label_async({
- name: name,
- color: generate_random_hex_color(),
- });
- loading = false;
- if (response.ok) {
- // Small pause to allow loading state to update everywhere.
- setTimeout(() => select_label(response.data.id), 50);
- }
- }
-
- export const functions = {
- get_selected,
- reset,
- load_labels: reload_labels,
- select_labels,
- select_label,
- };
-</script>
-
-<Dropdown
- entries={$labels}
- label="{$LL.views.entryForm.labels.labels()}"
- maxlength="50"
- createable={true}
- placeholder="{$LL.views.entryForm.labels.placeholder()}"
- multiple="{true}"
- id="labels-search"
- name="labels-search"
- on_create_async={on_create}
- noResultsText="{$LL.views.entryForm.labels.placeholder()}"
- errorText="{labelsError}"
- bind:this={DropdownExports}
- {loading}
-/>
diff --git a/old-apps/projects/src/app/pages/views/profile-modal.svelte b/old-apps/projects/src/app/pages/views/profile-modal.svelte
deleted file mode 100644
index 7560175..0000000
--- a/old-apps/projects/src/app/pages/views/profile-modal.svelte
+++ /dev/null
@@ -1,156 +0,0 @@
-<script>
- import {update_profile} from "$shared/lib/api/user";
- import Modal from "$shared/components/modal.svelte";
- import Alert from "$shared/components/alert.svelte";
- import Button from "$shared/components/button.svelte";
- import {is_email} from "$shared/lib/helpers";
- import {api_base} from "$shared/lib/configuration";
- import {get_session_data} from "$shared/lib/session";
-
- const archiveLink = api_base("_/api/account/archive");
-
- let modal;
- let understands = false;
-
- let formIsLoading = false;
- let formError;
-
- let username = get_session_data()?.profile.username;
- let usernameFieldMessage;
- let usernameFieldMessageClass = "color-error";
-
- let password;
- let passwordFieldMessage;
- let passwordFieldMessageClass = "color-error";
-
- async function submit_form(e) {
- e.preventDefault();
- if (!username && !password) {
- console.error("Not submitting becuase both values is empty");
- return;
- }
-
- usernameFieldMessage = "";
- passwordFieldMessage = "";
-
- if (username && !is_email(username)) {
- usernameFieldMessage = "Username has to be a valid email";
- return;
- }
-
- if (password && password?.length < 6) {
- passwordFieldMessage = "The new password must contain at least 6 characters";
- return;
- }
-
- formIsLoading = true;
-
- const response = await update_profile({
- username,
- password,
- });
-
- formIsLoading = false;
-
- if (response.ok) {
- if (password) {
- passwordFieldMessage = "Successfully updated";
- passwordFieldMessageClass = "color-success";
- password = "";
- }
- if (username) {
- usernameFieldMessage = "Successfully updated";
- usernameFieldMessageClass = "color-success";
- password = "";
- }
- } else {
- formError = response.data.title ?? "An unknown error occured";
- }
- }
-
- async function handle_delete_account_button_click() {
- alert("Not implemented");
- return;
- if (understands && confirm("Are you absolutely sure that you want to delete your account?")) {
- }
- }
-
- export const functions = {
- open() {
- modal.open();
- },
- close() {
- // modal.close();
- },
- };
-</script>
-
-<Modal title="Profile"
- bind:functions={modal}>
- <section class="margin-bottom-md">
- <p class="text-md margin-bottom-sm">Update your information</p>
- <form on:submit={submit_form}
- autocomplete="new-password">
- {#if formError}
- <small class="color-danger">{formError}</small>
- {/if}
- <div class="margin-bottom-sm">
- <label for="email"
- class="form-label margin-bottom-xxs">New username</label>
- <input type="email"
- class="form-control width-100%"
- id="email"
- placeholder={username}
- bind:value={username}/>
- {#if usernameFieldMessage}
- <small class={usernameFieldMessageClass}>{usernameFieldMessage}</small>
- {/if}
- </div>
- <div class="margin-bottom-sm">
- <label for="password"
- class="form-label margin-bottom-xxs">New password</label>
- <input type="password"
- class="form-control width-100%"
- id="password"
- bind:value={password}/>
- {#if passwordFieldMessage}
- <small class={passwordFieldMessageClass}>{passwordFieldMessage}</small>
- {/if}
- </div>
- <div class="flex justify-end">
- <Button text="Save"
- on:click={submit_form}
- variant="primary"
- loading={formIsLoading}/>
- </div>
- </form>
- </section>
- <section class="margin-bottom-md">
- <p class="text-md margin-bottom-sm">Download your data</p>
- <a class="btn btn--subtle"
- href={archiveLink}
- download>Click here to download your data</a>
- </section>
- <section>
- <p class="text-md margin-bottom-sm">Delete account</p>
- <div class="margin-bottom-sm">
- <Alert
- message="Deleting your account and data means that all of your data (entries, categories, etc.) will be unrecoverable forever.<br>You should probably download your data before continuing."
- type="info"
- />
- </div>
- <div class="form-check margin-bottom-sm">
- <input type="checkbox"
- class="checkbox"
- id="the-consequences"
- bind:checked={understands}/>
- <label for="the-consequences">I understand the consequences of deleting my account and data.</label>
- </div>
- <div class="flex justify-end">
- <Button text="Delete everything"
- variant="accent"
- disabled={!understands}
- on:click={handle_delete_account_button_click}/>
- </div>
- </section>
-</Modal>
diff --git a/old-apps/projects/src/app/pages/views/settings-categories-tile.svelte b/old-apps/projects/src/app/pages/views/settings-categories-tile.svelte
deleted file mode 100644
index 8d2480f..0000000
--- a/old-apps/projects/src/app/pages/views/settings-categories-tile.svelte
+++ /dev/null
@@ -1,126 +0,0 @@
-<script>
- import {IconNames} from "$shared/lib/configuration";
- import {onMount} from "svelte";
- import {
- delete_time_category,
- get_time_categories,
- } from "$shared/lib/api/time-entry";
- import Button from "$shared/components/button.svelte";
- import Tile from "$shared/components/tile.svelte";
- import {Table, THead, TBody, TCell, TRow} from "$shared/components/table";
- import LL from "$app/lib/i18n/i18n-svelte";
-
- let is_loading = true;
- let categories = [];
-
- $: active_categories = categories.filter(c => !c.archived);
- $: archived_categories = categories.filter(c => c.archived);
-
- async function load_categories() {
- is_loading = true;
- const response = await get_time_categories();
- if (response.status === 200) {
- categories = response.data;
- } else if (response.status === 204) {
- categories = [];
- console.log("Empty response when getting time categories");
- } else {
- categories = [];
- console.error("Error when getting time categories");
- }
- is_loading = false;
- }
-
- async function handle_edit_category_click(event) {
- }
-
- async function handle_delete_category_click(event) {
- const row = event.target.closest("tr");
- if (
- row &&
- row.dataset.id &&
- confirm($LL.views.settingsCategoriesTile.deleteAllConfirm())
- ) {
- const response = await delete_time_category(row.dataset.id);
- if (response.ok) {
- // svelte errors if we remove the row.
- row.classList.add("d-none");
- }
- }
- }
-
- onMount(() => {
- load_categories();
- });
-</script>
-
-<Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}">
- <h2 class="margin-bottom-xxs">{$LL.views.settingsCategoriesTile.categories()}</h2>
- {#if active_categories.length > 0 && archived_categories.length > 0}
- <nav class="s-tabs text-sm">
- <ul class="s-tabs__list">
- <li><a class="s-tabs__link s-tabs__link--current"
- href="#0">{$LL.views.settingsCategoriesTile.active()} ({active_categories.length})</a></li>
- <li><a class="s-tabs__link"
- href="#0">{$LL.views.settingsCategoriesTile.archived()} ({archived_categories.length})</a></li>
- </ul>
- </nav>
- {/if}
- <div class="max-width-100% overflow-auto">
- <Table class="text-sm width-100%">
- <THead class="text-left">
- <TCell type="th"
- thScope="col">
- {$LL.views.settingsCategoriesTile.name()}
- </TCell>
- <TCell type="th"
- thScope="col">
- {$LL.views.settingsCategoriesTile.color()}
- </TCell>
- <TCell type="th"
- thScope="col"
- style="width:50px"></TCell>
- </THead>
- <TBody class="text-left">
- {#if categories.length > 0}
- {#each categories as category}
- <TRow class="text-nowrap"
- data-id={category.id}>
- <TCell>
- {category.name}
- </TCell>
- <TCell>
- <span style="border-left: 3px solid {category.color}; background-color:{category.color}25;">
- {category.color}
- </span>
- </TCell>
- <TCell>
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- class="hide"
- icon_height="1.2rem"
- on:click={handle_edit_category_click}
- title="{$LL.views.settingsCategoriesTile.editEntry()}"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={handle_delete_category_click}
- title="{$LL.views.settingsCategoriesTile.deleteEntry()}"/>
-
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow>
- <TCell type="th"
- thScope="3">
- {$LL.views.settingsCategoriesTile.noCategories()}
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
-</Tile>
diff --git a/old-apps/projects/src/app/pages/views/settings-labels-tile.svelte b/old-apps/projects/src/app/pages/views/settings-labels-tile.svelte
deleted file mode 100644
index 3d5a567..0000000
--- a/old-apps/projects/src/app/pages/views/settings-labels-tile.svelte
+++ /dev/null
@@ -1,111 +0,0 @@
-<script>
- import {IconNames} from "$shared/lib/configuration";
- import {onMount} from "svelte";
- import labels, {reload_labels, delete_label_async} from "$app/lib/stores/labels";
- import Button from "$shared/components/button.svelte";
- import Tile from "$shared/components/tile.svelte";
- import {Table, THead, TBody, TCell, TRow} from "$shared/components/table";
- import LL from "$app/lib/i18n/i18n-svelte";
-
- let isLoadingLabels = true;
-
- $: active_labels = $labels.filter(c => !c.archived);
- $: archived_labels = $labels.filter(c => c.archived);
-
- async function load_labels() {
- isLoadingLabels = true;
- await reload_labels();
- isLoadingLabels = false;
- }
-
- async function handle_edit_label_click(event) {
- }
-
- async function handle_delete_label_click(event) {
- const row = event.target.closest("tr");
- if (
- row &&
- row.dataset.id &&
- confirm($LL.views.settingsLabelsTile.deleteAllConfirm())
- ) {
- await delete_label_async({id: row.dataset.id});
- row.classList.add("d-none");
- }
- }
-
- onMount(() => {
- load_labels();
- });
-</script>
-
-<Tile class="col-6@md col-12 {isLoadingLabels ? 'c-disabled loading' : ''}">
- <h2 class="margin-bottom-xxs">{$LL.views.settingsLabelsTile.labels()}</h2>
- {#if active_labels.length > 0 && archived_labels.length > 0}
- <nav class="s-tabs text-sm">
- <ul class="s-tabs__list">
- <li><a class="s-tabs__link s-tabs__link--current"
- href="#0">{$LL.views.settingsLabelsTile.active()} ({active_labels.length})</a></li>
- <li><a class="s-tabs__link"
- href="#0">{$LL.views.settingsLabelsTile.archived()} ({archived_labels.length})</a></li>
- </ul>
- </nav>
- {/if}
- <div class="max-width-100% overflow-auto">
- <Table class="text-sm width-100%">
- <THead class="text-left">
- <TCell type="th"
- thScope="row">
- {$LL.views.settingsLabelsTile.name()}
- </TCell>
- <TCell type="th"
- thScope="row">
- {$LL.views.settingsLabelsTile.color()}
- </TCell>
- <TCell type="th"
- thScope="row"
- style="width: 50px;">
- </TCell>
- </THead>
- <TBody class="text-left">
- {#if $labels.length > 0}
- {#each $labels as label}
- <TRow class="text-nowrap"
- dataId={label.id}>
- <TCell>
- {label.name}
- </TCell>
- <TCell>
- <span style="border-left: 3px solid {label.color}; background-color:{label.color}25;">
- {label.color}
- </span>
- </TCell>
- <TCell>
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- class="hide"
- icon_height="1.2rem"
- on:click={handle_edit_label_click}
- title="{$LL.views.settingsLabelsTile.editEntry()}"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={handle_delete_label_click}
- title="{$LL.views.settingsLabelsTile.deleteEntry()}"/>
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow>
- <TCell type="th"
- thScope="row"
- colspan="3">
- {$LL.views.settingsLabelsTile.noLabels()}
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
-</Tile>