From a640703f2da8815dc26ad1600a6f206be1624379 Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Wed, 1 Jun 2022 22:10:32 +0200 Subject: feat: Initial after clean slate --- apps/projects-web/src/app/index.d.ts | 48 +++ apps/projects-web/src/app/index.html | 63 ++++ apps/projects-web/src/app/index.scss | 38 ++ apps/projects-web/src/app/index.svelte | 56 +++ apps/projects-web/src/app/index.ts | 16 + .../src/app/lib/services/user-service.ts | 21 ++ apps/projects-web/src/app/lib/stores/categories.ts | 44 +++ apps/projects-web/src/app/lib/stores/entries.ts | 74 ++++ apps/projects-web/src/app/lib/stores/labels.ts | 44 +++ apps/projects-web/src/app/pages/_layout.svelte | 79 +++++ apps/projects-web/src/app/pages/data.svelte | 392 +++++++++++++++++++++ apps/projects-web/src/app/pages/home.svelte | 167 +++++++++ apps/projects-web/src/app/pages/not-found.svelte | 24 ++ apps/projects-web/src/app/pages/settings.svelte | 12 + .../projects-web/src/app/pages/ui-workbench.svelte | 48 +++ .../src/app/pages/views/category-form/index.svelte | 144 ++++++++ .../app/pages/views/data-table-paginator.svelte | 107 ++++++ .../src/app/pages/views/entry-form/index.svelte | 196 +++++++++++ .../views/entry-form/sections/category.svelte | 75 ++++ .../views/entry-form/sections/date-time.svelte | 165 +++++++++ .../pages/views/entry-form/sections/labels.svelte | 65 ++++ .../src/app/pages/views/profile-modal.svelte | 156 ++++++++ .../pages/views/settings-categories-tile.svelte | 127 +++++++ .../app/pages/views/settings-labels-tile.svelte | 112 ++++++ 24 files changed, 2273 insertions(+) create mode 100644 apps/projects-web/src/app/index.d.ts create mode 100644 apps/projects-web/src/app/index.html create mode 100644 apps/projects-web/src/app/index.scss create mode 100644 apps/projects-web/src/app/index.svelte create mode 100644 apps/projects-web/src/app/index.ts create mode 100644 apps/projects-web/src/app/lib/services/user-service.ts create mode 100644 apps/projects-web/src/app/lib/stores/categories.ts create mode 100644 apps/projects-web/src/app/lib/stores/entries.ts create mode 100644 apps/projects-web/src/app/lib/stores/labels.ts create mode 100644 apps/projects-web/src/app/pages/_layout.svelte create mode 100644 apps/projects-web/src/app/pages/data.svelte create mode 100644 apps/projects-web/src/app/pages/home.svelte create mode 100644 apps/projects-web/src/app/pages/not-found.svelte create mode 100644 apps/projects-web/src/app/pages/settings.svelte create mode 100644 apps/projects-web/src/app/pages/ui-workbench.svelte create mode 100644 apps/projects-web/src/app/pages/views/category-form/index.svelte create mode 100644 apps/projects-web/src/app/pages/views/data-table-paginator.svelte create mode 100644 apps/projects-web/src/app/pages/views/entry-form/index.svelte create mode 100644 apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte create mode 100644 apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte create mode 100644 apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte create mode 100644 apps/projects-web/src/app/pages/views/profile-modal.svelte create mode 100644 apps/projects-web/src/app/pages/views/settings-categories-tile.svelte create mode 100644 apps/projects-web/src/app/pages/views/settings-labels-tile.svelte (limited to 'apps/projects-web/src/app') diff --git a/apps/projects-web/src/app/index.d.ts b/apps/projects-web/src/app/index.d.ts new file mode 100644 index 0000000..c044583 --- /dev/null +++ b/apps/projects-web/src/app/index.d.ts @@ -0,0 +1,48 @@ +/* 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/apps/projects-web/src/app/index.html b/apps/projects-web/src/app/index.html new file mode 100644 index 0000000..7e0b0e1 --- /dev/null +++ b/apps/projects-web/src/app/index.html @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + Time Tracker + + + + + + + + +
+ + + + + diff --git a/apps/projects-web/src/app/index.scss b/apps/projects-web/src/app/index.scss new file mode 100644 index 0000000..4794787 --- /dev/null +++ b/apps/projects-web/src/app/index.scss @@ -0,0 +1,38 @@ +@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'; diff --git a/apps/projects-web/src/app/index.svelte b/apps/projects-web/src/app/index.svelte new file mode 100644 index 0000000..9dd2bf8 --- /dev/null +++ b/apps/projects-web/src/app/index.svelte @@ -0,0 +1,56 @@ + + + + +You seem to be offline, please check your internet connection. + { + document.getElementById("loader").style.display = "inline-block"; + }} + on:routeLoaded={() => { + document.getElementById("loader").style.display = "none"; + }} +/> diff --git a/apps/projects-web/src/app/index.ts b/apps/projects-web/src/app/index.ts new file mode 100644 index 0000000..febb583 --- /dev/null +++ b/apps/projects-web/src/app/index.ts @@ -0,0 +1,16 @@ +// @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/apps/projects-web/src/app/lib/services/user-service.ts b/apps/projects-web/src/app/lib/services/user-service.ts new file mode 100644 index 0000000..7bffa49 --- /dev/null +++ b/apps/projects-web/src/app/lib/services/user-service.ts @@ -0,0 +1,21 @@ +import {delete_account, logout} from "$shared/lib/api/user"; +import {accounts_base} from "$shared/lib/configuration"; +import {clear_session_data} 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 logout(); + clear_session_data(); + clear_categories(); + clear_labels(); + clear_entries(); + location.replace(accounts_base("#/login" + (reason ? "?" + reason : ""))); +} + +export async function delete_user() { + await delete_account(); + clear_session_data(); + location.replace(accounts_base("#/login?deleted")); +} diff --git a/apps/projects-web/src/app/lib/stores/categories.ts b/apps/projects-web/src/app/lib/stores/categories.ts new file mode 100644 index 0000000..2a63c42 --- /dev/null +++ b/apps/projects-web/src/app/lib/stores/categories.ts @@ -0,0 +1,44 @@ +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>([]); + +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 { + 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/apps/projects-web/src/app/lib/stores/entries.ts b/apps/projects-web/src/app/lib/stores/entries.ts new file mode 100644 index 0000000..e933568 --- /dev/null +++ b/apps/projects-web/src/app/lib/stores/entries.ts @@ -0,0 +1,74 @@ +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>([]); + +export function get_time_entry(id: string): TimeEntryDto { + return get(entries).find(c => c.id === id); +} + +export async function reload_entries(query: TimeEntryQuery): Promise { + 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): Array { + 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 { + 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 { + 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 { + 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/apps/projects-web/src/app/lib/stores/labels.ts b/apps/projects-web/src/app/lib/stores/labels.ts new file mode 100644 index 0000000..d5ffaa9 --- /dev/null +++ b/apps/projects-web/src/app/lib/stores/labels.ts @@ -0,0 +1,44 @@ +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>([]); + +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 { + 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/apps/projects-web/src/app/pages/_layout.svelte b/apps/projects-web/src/app/pages/_layout.svelte new file mode 100644 index 0000000..24a9370 --- /dev/null +++ b/apps/projects-web/src/app/pages/_layout.svelte @@ -0,0 +1,79 @@ + + + + + + +
+ +
diff --git a/apps/projects-web/src/app/pages/data.svelte b/apps/projects-web/src/app/pages/data.svelte new file mode 100644 index 0000000..070b98b --- /dev/null +++ b/apps/projects-web/src/app/pages/data.svelte @@ -0,0 +1,392 @@ + + + EditEntryForm.reset()}> + + + +
+
+ +
+ + + + + +
+
+ + {#if currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE} +
+ Date: + + + +
+ {/if} + + {#if currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE} +
+ From: + + (currentDateRangeFilter.from = e.target.value)}/> + +
+ +
+ To: + + (currentDateRangeFilter.to = e.target.value)}/> + +
+ {/if} + +
+
+
+ + + + +
+ + + +
+ + +
+
+ + +
+ Date +
+ +
+
+
+ + +
+ Duration +
+
+ + +
+ Category +
+
+ + +
+ Description +
+
+ + + + {#if entries.length > 0} + {#each entries as entry} + + +
+ + +
+
+ +
{entry.date.toLocaleString()}
+
+ +
+                                    
+ {entry.start.toLocaleString(undefined, {timeStyle: "short"})} + - + {entry.stop.toLocaleString(undefined, {timeStyle: "short"})} +
+
+
+ + {entry.category.name} + + + {entry.description ?? ""} + + +
+
+
+
+

+ {#if durationSummary} + {durationSummary} + {:else} + No entries + {/if} +

+ + +
+
+
diff --git a/apps/projects-web/src/app/pages/home.svelte b/apps/projects-web/src/app/pages/home.svelte new file mode 100644 index 0000000..c3e7af4 --- /dev/null +++ b/apps/projects-web/src/app/pages/home.svelte @@ -0,0 +1,167 @@ + + + +
+ +

New entry

+ +
+
+ +

{timeLoggedTodayString}

+

Logged time today

+
{currentTime}
+

Current time

+
+ + +

Stopwatch

+
+
+ +

Today's entries

+
+ + + + Category + + + Timespan + + + + + {#if timeEntries.length > 0} + {#each timeEntries as entry} + + + + {entry.category?.name} + + + + {entry.start.toLocaleString(undefined, {timeStyle: "short"})} + - + {entry.stop.toLocaleString(undefined, {timeStyle: "short"})} + + + +
+
+
+
+
+
diff --git a/apps/projects-web/src/app/pages/not-found.svelte b/apps/projects-web/src/app/pages/not-found.svelte new file mode 100644 index 0000000..46d0d1d --- /dev/null +++ b/apps/projects-web/src/app/pages/not-found.svelte @@ -0,0 +1,24 @@ + + + + +
+
404
+

Page not found!

+ Go to front +
diff --git a/apps/projects-web/src/app/pages/settings.svelte b/apps/projects-web/src/app/pages/settings.svelte new file mode 100644 index 0000000..ca9fd47 --- /dev/null +++ b/apps/projects-web/src/app/pages/settings.svelte @@ -0,0 +1,12 @@ + + + +
+ + +
+
diff --git a/apps/projects-web/src/app/pages/ui-workbench.svelte b/apps/projects-web/src/app/pages/ui-workbench.svelte new file mode 100644 index 0000000..5e92c9d --- /dev/null +++ b/apps/projects-web/src/app/pages/ui-workbench.svelte @@ -0,0 +1,48 @@ + + +
+
+ + +
+
diff --git a/apps/projects-web/src/app/pages/views/category-form/index.svelte b/apps/projects-web/src/app/pages/views/category-form/index.svelte new file mode 100644 index 0000000..e8c0f94 --- /dev/null +++ b/apps/projects-web/src/app/pages/views/category-form/index.svelte @@ -0,0 +1,144 @@ + + +
+
+ +
+
+
+ + + {#if dough.fields.name.error} + {dough.fields.name.error} + {/if} +
+
+ + + {#if dough.fields.color.error} + {dough.fields.color.error} + {/if} +
+
+
+ + dough.fields.labels.create({name})}/> + {#if dough.fields.labels.error} + {dough.fields.labels.error} + {/if} +
+
diff --git a/apps/projects-web/src/app/pages/views/data-table-paginator.svelte b/apps/projects-web/src/app/pages/views/data-table-paginator.svelte new file mode 100644 index 0000000..7696ca2 --- /dev/null +++ b/apps/projects-web/src/app/pages/views/data-table-paginator.svelte @@ -0,0 +1,107 @@ + + + diff --git a/apps/projects-web/src/app/pages/views/entry-form/index.svelte b/apps/projects-web/src/app/pages/views/entry-form/index.svelte new file mode 100644 index 0000000..cb974ed --- /dev/null +++ b/apps/projects-web/src/app/pages/views/entry-form/index.svelte @@ -0,0 +1,196 @@ + + +
functions.reset()}> +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ {#if entryId} +
+
diff --git a/apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte b/apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte new file mode 100644 index 0000000..f98c045 --- /dev/null +++ b/apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte @@ -0,0 +1,75 @@ + + + + diff --git a/apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte b/apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte new file mode 100644 index 0000000..c91e014 --- /dev/null +++ b/apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte @@ -0,0 +1,165 @@ + + +
+
+ + + {#if dateError} + {dateError} + {/if} +
+
+ + + {#if fromTimeError} + {fromTimeError} + {/if} +
+
+ + + {#if toTimeError} + {toTimeError} + {/if} +
+
diff --git a/apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte b/apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte new file mode 100644 index 0000000..06c703d --- /dev/null +++ b/apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte @@ -0,0 +1,65 @@ + + + diff --git a/apps/projects-web/src/app/pages/views/profile-modal.svelte b/apps/projects-web/src/app/pages/views/profile-modal.svelte new file mode 100644 index 0000000..839b59d --- /dev/null +++ b/apps/projects-web/src/app/pages/views/profile-modal.svelte @@ -0,0 +1,156 @@ + + + +
+

Update your information

+
+ {#if formError} + {formError} + {/if} +
+ + + {#if usernameFieldMessage} + {usernameFieldMessage} + {/if} +
+
+ + + {#if passwordFieldMessage} + {passwordFieldMessage} + {/if} +
+
+
+
+
+
+

Download your data

+ Click here to download your data +
+
+

Delete account

+
+ +
+
+ + +
+
+
+
+
diff --git a/apps/projects-web/src/app/pages/views/settings-categories-tile.svelte b/apps/projects-web/src/app/pages/views/settings-categories-tile.svelte new file mode 100644 index 0000000..890609a --- /dev/null +++ b/apps/projects-web/src/app/pages/views/settings-categories-tile.svelte @@ -0,0 +1,127 @@ + + + +

Categories

+ {#if active_categories.length > 0 && archived_categories.length > 0} + + {/if} +
+ + + + Name + + + Color + + + + + {#if categories.length > 0} + {#each categories as category} + + + {category.name} + + + + {category.color} + + + + +
+
+
diff --git a/apps/projects-web/src/app/pages/views/settings-labels-tile.svelte b/apps/projects-web/src/app/pages/views/settings-labels-tile.svelte new file mode 100644 index 0000000..f59e233 --- /dev/null +++ b/apps/projects-web/src/app/pages/views/settings-labels-tile.svelte @@ -0,0 +1,112 @@ + + + +

Labels

+ {#if active_labels.length > 0 && archived_labels.length > 0} + + {/if} +
+ + + + Name + + + Color + + + + + + {#if $labels.length > 0} + {#each $labels as label} + + + {label.name} + + + + {label.color} + + + + +
+
+
-- cgit v1.3