diff --git a/.prettierignore b/.prettierignore index 3897265..75980e8 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,3 +11,9 @@ node_modules pnpm-lock.yaml package-lock.json yarn.lock + +# Custom: +/src-tauri +/src/old_routes +/old/ +/.github/ diff --git a/src/lib/crud/Dashboard.ts b/src/lib/crud/Dashboard.ts index 2e3bb3c..3af67f5 100644 --- a/src/lib/crud/Dashboard.ts +++ b/src/lib/crud/Dashboard.ts @@ -1,45 +1,39 @@ import { DashboardDefinition, UrlAction } from '@orbitale/svelte-admin'; import Home from 'carbon-icons-svelte/lib/Home.svelte'; -import PiggyBank from "carbon-icons-svelte/lib/PiggyBank.svelte"; -import ChartLogisticRegression from "carbon-icons-svelte/lib/ChartLogisticRegression.svelte"; -import ListBoxes from "carbon-icons-svelte/lib/ListBoxes.svelte"; -import Tag from "carbon-icons-svelte/lib/Tag.svelte"; -import TagGroup from "carbon-icons-svelte/lib/TagGroup.svelte"; -import FetchUpload from "carbon-icons-svelte/lib/FetchUpload.svelte"; -import ListCheckedMirror from "carbon-icons-svelte/lib/ListCheckedMirror.svelte"; +import PiggyBank from 'carbon-icons-svelte/lib/PiggyBank.svelte'; +import ChartLogisticRegression from 'carbon-icons-svelte/lib/ChartLogisticRegression.svelte'; +import ListBoxes from 'carbon-icons-svelte/lib/ListBoxes.svelte'; +import Tag from 'carbon-icons-svelte/lib/Tag.svelte'; +import TagGroup from 'carbon-icons-svelte/lib/TagGroup.svelte'; +import FetchUpload from 'carbon-icons-svelte/lib/FetchUpload.svelte'; +import ListCheckedMirror from 'carbon-icons-svelte/lib/ListCheckedMirror.svelte'; import OperationCrud from '$lib/crud/cruds/OperationCrud'; -import BankAccountsCrud from "$lib/crud/cruds/BankAccountsCrud"; -import TagRulesCrud from "$lib/crud/cruds/TagRulesCrud"; -import TagsCrud from "$lib/crud/cruds/TagsCrud"; -import TriageCrud from "$lib/crud/cruds/TriageCrud"; +import BankAccountsCrud from '$lib/crud/cruds/BankAccountsCrud'; +import TagRulesCrud from '$lib/crud/cruds/TagRulesCrud'; +import TagsCrud from '$lib/crud/cruds/TagsCrud'; +import TriageCrud from '$lib/crud/cruds/TriageCrud'; export const dashboard = new DashboardDefinition({ - adminConfig: { - defaultLocale: 'en', - rootUrl: '/crud/', - head: { - brandName: 'Compotes', - appName: '' - } - }, + adminConfig: { + defaultLocale: 'en', + rootUrl: '/crud/', + head: { + brandName: 'Compotes', + appName: '' + } + }, - sideMenu: [ - new UrlAction('Homepage', '/', Home), - // new UrlAction('Analytics', '/analytics', ChartLogisticRegression), - new UrlAction('Operations', '/crud/operations/list', ListBoxes), - new UrlAction('Tags', '/crud/tags/list', Tag), - new UrlAction('Tag rules', '/crud/tag-rules/list', TagGroup), - new UrlAction('Triage', '/crud/triage/list', ListCheckedMirror), - new UrlAction('Bank accounts', '/crud/bank-accounts/list', PiggyBank), - new UrlAction('Import', '/import', FetchUpload), - ], + sideMenu: [ + new UrlAction('Homepage', '/', Home), + // new UrlAction('Analytics', '/analytics', ChartLogisticRegression), + new UrlAction('Operations', '/crud/operations/list', ListBoxes), + new UrlAction('Tags', '/crud/tags/list', Tag), + new UrlAction('Tag rules', '/crud/tag-rules/list', TagGroup), + new UrlAction('Triage', '/crud/triage/list', ListCheckedMirror), + new UrlAction('Bank accounts', '/crud/bank-accounts/list', PiggyBank), + new UrlAction('Import', '/import', FetchUpload) + ], - cruds: [ - OperationCrud, - BankAccountsCrud, - TagRulesCrud, - TagsCrud, - TriageCrud, - ] + cruds: [OperationCrud, BankAccountsCrud, TagRulesCrud, TagsCrud, TriageCrud] }); diff --git a/src/lib/crud/cruds/BankAccountsCrud.ts b/src/lib/crud/cruds/BankAccountsCrud.ts index f350718..c706661 100644 --- a/src/lib/crud/cruds/BankAccountsCrud.ts +++ b/src/lib/crud/cruds/BankAccountsCrud.ts @@ -1,70 +1,95 @@ import { - CallbackStateProcessor, - CallbackStateProvider, - CrudDefinition, Edit, - List, - TextField, - UrlAction, - type RequestParameters, - type CrudOperation, - type StateProcessorInput, -} from "@orbitale/svelte-admin"; + CallbackStateProcessor, + CallbackStateProvider, + CrudDefinition, + Edit, + List, + TextField, + UrlAction, + type RequestParameters, + type CrudOperation, + type StateProcessorInput +} from '@orbitale/svelte-admin'; -import {createBankAccount, getBankAccountById, getBankAccounts, updateBankAccount} from "$lib/db/bank_accounts"; -import type BankAccount from "$lib/entities/BankAccount"; -import {goto} from "$app/navigation"; -import {success} from "$lib/utils/message"; +import { + createBankAccount, + getBankAccountById, + getBankAccounts, + updateBankAccount +} from '$lib/db/bank_accounts'; +import type BankAccount from '$lib/entities/BankAccount'; +import { goto } from '$app/navigation'; +import { success } from '$lib/utils/message'; const baseFields = [ - new TextField('name', 'Name'), - new TextField('slug', 'Identifier', {disabled: true}), - new TextField('currency', 'Currency'), + new TextField('name', 'Name'), + new TextField('slug', 'Identifier', { disabled: true }), + new TextField('currency', 'Currency') ]; export default new CrudDefinition({ - name: 'bank-accounts', - defaultOperationName: "list", - label: {plural: "BankAccounts", singular: "BankAccount"}, - // minStateLoadingTimeMs: 0, + name: 'bank-accounts', + defaultOperationName: 'list', + label: { plural: 'BankAccounts', singular: 'BankAccount' }, + // minStateLoadingTimeMs: 0, + + operations: [ + new List([...baseFields], [new UrlAction('Edit', '/crud/bank-accounts/edit')]), + new Edit(baseFields) + ], + + stateProvider: new CallbackStateProvider( + async (operation: CrudOperation, requestParameters: RequestParameters) => { + if (typeof window === 'undefined') { + // SSR, can't call Tauri API then. + return Promise.resolve([]); + } - operations: [ - new List([...baseFields], - [ - new UrlAction('Edit', '/crud/bank-accounts/edit'), - ]), - new Edit(baseFields), - ], + if (operation.name === 'list') { + return getBankAccounts(); + } - stateProvider: new CallbackStateProvider(async (operation: CrudOperation, requestParameters: RequestParameters) => { - if (typeof window === 'undefined') { - // SSR, can't call Tauri API then. - return Promise.resolve([]); - } + if (operation.name === 'view' || operation.name === 'edit') { + return getBankAccountById(Number(requestParameters.id)); + } - if (operation.name === 'list') { - return getBankAccounts(); - } + return Promise.resolve(null); + } + ), - if (operation.name === 'view' || operation.name === 'edit') { - return getBankAccountById(Number(requestParameters.id)); - } + stateProcessor: new CallbackStateProcessor( + async ( + data: StateProcessorInput, + operation: CrudOperation, + requestParameters: RequestParameters + ) => { + if (operation.name === 'new') { + if (!data) { + throw new Error('Cannot create new object: empty data.'); + } + if (Array.isArray(data)) { + throw new Error('Cannot update data as array for this action.'); + } - return Promise.resolve(null); - }), + return createBankAccount(data); + } - stateProcessor: new CallbackStateProcessor(async (data: StateProcessorInput, operation: CrudOperation, requestParameters: RequestParameters) => { - if (operation.name === 'new') { - return createBankAccount(data); - } + if (operation.name === 'edit') { + if (!data) { + throw new Error('Cannot create new object: empty data.'); + } + if (Array.isArray(data)) { + throw new Error('Cannot update data as array for this action.'); + } - if (operation.name === 'edit') { - data.id = Number(requestParameters.id); + data.id = Number(requestParameters.id); - await updateBankAccount(data); - success('Success!'); - await goto('/crud/bank-accounts/list'); + await updateBankAccount(data); + success('Success!'); + await goto('/crud/bank-accounts/list'); - return; - } - }) + return; + } + } + ) }); diff --git a/src/lib/crud/cruds/OperationCrud.ts b/src/lib/crud/cruds/OperationCrud.ts index b2d7684..71e547e 100644 --- a/src/lib/crud/cruds/OperationCrud.ts +++ b/src/lib/crud/cruds/OperationCrud.ts @@ -1,83 +1,91 @@ import { - CallbackStateProcessor, - CallbackStateProvider, - CheckboxField, - CrudDefinition, - DateField, - List, - NumberField, - PaginatedResults, - TextField, - UrlAction, - View, - type RequestParameters, - type CrudOperation, - type ListOperationOptions, - KeyValueObjectField -} from "@orbitale/svelte-admin"; + CallbackStateProcessor, + CallbackStateProvider, + CheckboxField, + CrudDefinition, + DateField, + List, + NumberField, + PaginatedResults, + TextField, + UrlAction, + View, + type RequestParameters, + type CrudOperation, + type ListOperationOptions, + KeyValueObjectField +} from '@orbitale/svelte-admin'; -import {getOperationById, getOperations, getOperationsCount} from "$lib/db/operations"; -import type Operation from "$lib/entities/Operation"; +import { getOperationById, getOperations, getOperationsCount } from '$lib/db/operations'; +import type Operation from '$lib/entities/Operation'; export default new CrudDefinition({ - name: 'operations', - defaultOperationName: "list", - label: {plural: "Operations", singular: "Operation"}, - // minStateLoadingTimeMs: 0, + name: 'operations', + defaultOperationName: 'list', + label: { plural: 'Operations', singular: 'Operation' }, + // minStateLoadingTimeMs: 0, - operations: [ - new List( - [ - new DateField('operation_date', 'Date'), - new TextField('op_type', 'Type 1'), - new TextField('type_display', 'Type 2'), - new TextField('details', 'Details'), - new TextField('tags', 'Tags'), - new NumberField('amount_display', 'Montant'), - ], - [ - new UrlAction('View', '/crud/operations/view'), - ], - { - pagination: { - enabled: true, - itemsPerPage: 20, - } - }), - new View([ - new TextField('id', 'ID'), - new DateField('operation_date', 'Date'), - new TextField('op_type', 'Type 1'), - new TextField('type_display', 'Type 2'), - new TextField('details', 'Details'), - new NumberField('amount_in_cents', 'Montant (in cents)'), - new NumberField('amount', 'Montant'), - new NumberField('hash', 'Hash'), - new TextField('state', 'State'), - new KeyValueObjectField('bank_account', 'Bank account', 'name'), - new TextField('tags', 'Tags'), - new CheckboxField('ignored_from_charts', 'Is ignored from charts'), - ]), - ], + operations: [ + new List( + [ + new DateField('operation_date', 'Date'), + new TextField('op_type', 'Type 1'), + new TextField('type_display', 'Type 2'), + new TextField('details', 'Details'), + new TextField('tags', 'Tags'), + new NumberField('amount_display', 'Montant') + ], + [new UrlAction('View', '/crud/operations/view')], + { + pagination: { + enabled: true, + itemsPerPage: 20 + } + } + ), + new View([ + new TextField('id', 'ID'), + new DateField('operation_date', 'Date'), + new TextField('op_type', 'Type 1'), + new TextField('type_display', 'Type 2'), + new TextField('details', 'Details'), + new NumberField('amount_in_cents', 'Montant (in cents)'), + new NumberField('amount', 'Montant'), + new NumberField('hash', 'Hash'), + new TextField('state', 'State'), + new KeyValueObjectField('bank_account', 'Bank account', 'name'), + new TextField('tags', 'Tags'), + new CheckboxField('ignored_from_charts', 'Is ignored from charts') + ]) + ], - stateProvider: new CallbackStateProvider(async (operation: CrudOperation, requestParameters: RequestParameters) => { - if (typeof window === 'undefined') { - // SSR, can't call Tauri API then. - return Promise.resolve([]); - } + stateProvider: new CallbackStateProvider( + async (operation: CrudOperation, requestParameters: RequestParameters) => { + if (typeof window === 'undefined') { + // SSR, can't call Tauri API then. + return Promise.resolve([]); + } - if (operation.name === 'list') { - const options: ListOperationOptions = operation.options; - const results = await getOperations(Number(requestParameters.page)||1); - const numberOfItems = await getOperationsCount(null); - return Promise.resolve(new PaginatedResults(Number(requestParameters.page), numberOfItems / Number(options.pagination?.itemsPerPage||10), numberOfItems, results)); - } + if (operation.name === 'list') { + const options: ListOperationOptions = operation.options; + const results = await getOperations(Number(requestParameters.page) || 1); + const numberOfItems = await getOperationsCount(null); + return Promise.resolve( + new PaginatedResults( + Number(requestParameters.page), + numberOfItems / Number(options.pagination?.itemsPerPage || 10), + numberOfItems, + results + ) + ); + } - if (operation.name === 'view' || operation.name === 'edit') { - return getOperationById(Number(requestParameters.id)); - } + if (operation.name === 'view' || operation.name === 'edit') { + return getOperationById(Number(requestParameters.id)); + } - return Promise.resolve(null); - }), - stateProcessor: new CallbackStateProcessor(() => {}) + return Promise.resolve(null); + } + ), + stateProcessor: new CallbackStateProcessor(() => {}) }); diff --git a/src/lib/crud/cruds/TagRulesCrud.ts b/src/lib/crud/cruds/TagRulesCrud.ts index b499514..1d8490a 100644 --- a/src/lib/crud/cruds/TagRulesCrud.ts +++ b/src/lib/crud/cruds/TagRulesCrud.ts @@ -1,80 +1,104 @@ import { - CallbackStateProcessor, - CallbackStateProvider, - CrudDefinition, Edit, - List, New, - TextField, ToggleField, - UrlAction, -} from "@orbitale/svelte-admin"; -import type {RequestParameters} from "@orbitale/svelte-admin/dist/request"; -import type {CrudOperation} from "@orbitale/svelte-admin/dist/Crud/Operations"; -import type {StateProcessorInput} from "@orbitale/svelte-admin/dist/State/Processor"; + CallbackStateProcessor, + CallbackStateProvider, + CrudDefinition, + Edit, + List, + New, + TextField, + ToggleField, + UrlAction, + type RequestParameters, + type CrudOperation, + type StateProcessorInput +} from '@orbitale/svelte-admin'; -import {createTagRule, getTagRuleById, getTagRules, updateTagRule} from "$lib/db/tag_rules"; -import TagRule from "$lib/entities/TagRule"; -import {goto} from "$app/navigation"; -import {success} from "$lib/utils/message"; +import { createTagRule, getTagRuleById, getTagRules, updateTagRule } from '$lib/db/tag_rules'; +import TagRule from '$lib/entities/TagRule'; +import { goto } from '$app/navigation'; +import { success } from '$lib/utils/message'; +import type Tag from '$lib/entities/Tag'; const baseFields = [ - new TextField('tags', 'Tags'), - new TextField('matching_pattern', 'Matching pattern'), - new ToggleField('is_regex', 'Regex'), + new TextField('tags', 'Tags'), + new TextField('matching_pattern', 'Matching pattern'), + new ToggleField('is_regex', 'Regex') ]; export default new CrudDefinition({ - name: 'tag-rules', - defaultOperationName: "list", - label: {plural: "TagRules", singular: "TagRule"}, - // minStateLoadingTimeMs: 0, + name: 'tag-rules', + defaultOperationName: 'list', + label: { plural: 'TagRules', singular: 'TagRule' }, + // minStateLoadingTimeMs: 0, - operations: [ - new List([new TextField('id', 'ID'), ...baseFields], - [ - new UrlAction('Edit', '/crud/tag-rules/edit'), - ], { - globalActions: [ - new UrlAction('New', '/crud/tag-rules/new'), - ], - }), - new New(baseFields), - new Edit(baseFields), - ], + operations: [ + new List( + [new TextField('id', 'ID'), ...baseFields], + [new UrlAction('Edit', '/crud/tag-rules/edit')], + { + globalActions: [new UrlAction('New', '/crud/tag-rules/new')] + } + ), + new New(baseFields), + new Edit(baseFields) + ], - stateProvider: new CallbackStateProvider(async (operation: CrudOperation, requestParameters: RequestParameters) => { - if (typeof window === 'undefined') { - // SSR, can't call Tauri API then. - return Promise.resolve([]); - } + stateProvider: new CallbackStateProvider( + async (operation: CrudOperation, requestParameters: RequestParameters) => { + if (typeof window === 'undefined') { + // SSR, can't call Tauri API then. + return Promise.resolve([]); + } - if (operation.name === 'list') { - return getTagRules(); - } + if (operation.name === 'list') { + return getTagRules(); + } - if (operation.name === 'view' || operation.name === 'edit') { - return getTagRuleById(requestParameters.id); - } + if (operation.name === 'view' || operation.name === 'edit') { + return getTagRuleById(String(requestParameters.id)); + } - return Promise.resolve(null); - }), + return Promise.resolve(null); + } + ), - stateProcessor: new CallbackStateProcessor(async (data: StateProcessorInput, operation: CrudOperation, requestParameters: RequestParameters) => { - if (operation.name === 'new' || operation.name === 'edit') { - data.id = parseInt(requestParameters.id || 0, 10); - // TODO FIXME : remove this and use proper entity injection! - data.is_regex = !!data.is_regex; - data.tags = data.tags.replace(/[^0-9,]/, '').split(',').map((i: string) => parseInt(i, 10)); - const tag_rule = TagRule.fromJson(data); + stateProcessor: new CallbackStateProcessor( + async ( + data: StateProcessorInput, + operation: CrudOperation, + requestParameters: RequestParameters + ) => { + if (operation.name === 'new' || operation.name === 'edit') { + if (!data) { + throw new Error('Cannot create new object: empty data.'); + } + if (Array.isArray(data)) { + throw new Error('Cannot update data as array for this action.'); + } - if (operation.name === 'new') { - await createTagRule(tag_rule); - } - if (operation.name === 'edit') { - await updateTagRule(tag_rule); - } + data.id = Number(requestParameters.id || 0); + // TODO FIXME : remove this and use proper entity injection! + data.is_regex = Boolean(data.is_regex); + if (!Array.isArray(data.tags)) { + // @ts-ignore + data.tags = String(data.tags) + .replace(/[^0-9,]/, '') + .split(',') + .map((i: string) => Number(i)); + } + const tag_rule = TagRule.fromJson(data); - success('Success!'); - await goto('/crud/tag-rules/list'); - return; - } - }) + if (operation.name === 'new') { + await createTagRule(tag_rule); + } + if (operation.name === 'edit') { + await updateTagRule(tag_rule); + } + + success('Success!'); + await goto('/crud/tag-rules/list'); + return; + } + } + ) }); diff --git a/src/lib/crud/cruds/TagsCrud.ts b/src/lib/crud/cruds/TagsCrud.ts index 759576f..3c371dc 100644 --- a/src/lib/crud/cruds/TagsCrud.ts +++ b/src/lib/crud/cruds/TagsCrud.ts @@ -1,76 +1,90 @@ import { - CallbackStateProcessor, - CallbackStateProvider, - CrudDefinition, Edit, - List, New, - TextField, - UrlAction, -} from "@orbitale/svelte-admin"; -import type {RequestParameters} from "@orbitale/svelte-admin/dist/request"; -import type {CrudOperation} from "@orbitale/svelte-admin/dist/Crud/Operations"; -import type {StateProcessorInput} from "@orbitale/svelte-admin/dist/State/Processor"; + CallbackStateProcessor, + CallbackStateProvider, + CrudDefinition, + Edit, + List, + New, + TextField, + UrlAction, + type RequestParameters, + type CrudOperation, + type StateProcessorInput +} from '@orbitale/svelte-admin'; -import {createTag, getTagById, getTags, updateTag} from "$lib/db/tags"; -import Tag from "$lib/entities/Tag"; -import {goto} from "$app/navigation"; -import {success} from "$lib/utils/message"; +import { createTag, getTagById, getTags, updateTag } from '$lib/db/tags'; +import Tag from '$lib/entities/Tag'; +import { goto } from '$app/navigation'; +import { success } from '$lib/utils/message'; -const baseFields = [ - new TextField('name', 'Name'), -]; +const baseFields = [new TextField('name', 'Name')]; export default new CrudDefinition({ - name: 'tags', - defaultOperationName: "list", - label: {plural: "Tags", singular: "Tag"}, - // minStateLoadingTimeMs: 0, + name: 'tags', + defaultOperationName: 'list', + label: { plural: 'Tags', singular: 'Tag' }, + // minStateLoadingTimeMs: 0, - operations: [ - new List([new TextField('id', 'ID'), ...baseFields], - [ - new UrlAction('Edit', '/crud/tags/edit'), - ], { - globalActions: [ - new UrlAction('New', '/crud/tags/new'), - ], - }), - new New(baseFields), - new Edit(baseFields), - ], + operations: [ + new List( + [new TextField('id', 'ID'), ...baseFields], + [new UrlAction('Edit', '/crud/tags/edit')], + { + globalActions: [new UrlAction('New', '/crud/tags/new')] + } + ), + new New(baseFields), + new Edit(baseFields) + ], - stateProvider: new CallbackStateProvider(async (operation: CrudOperation, requestParameters: RequestParameters) => { - if (typeof window === 'undefined') { - // SSR, can't call Tauri API then. - return Promise.resolve([]); - } + stateProvider: new CallbackStateProvider( + async (operation: CrudOperation, requestParameters: RequestParameters) => { + if (typeof window === 'undefined') { + // SSR, can't call Tauri API then. + return Promise.resolve([]); + } - if (operation.name === 'list') { - return getTags(); - } + if (operation.name === 'list') { + return getTags(); + } - if (operation.name === 'view' || operation.name === 'edit') { - return getTagById(requestParameters.id); - } + if (operation.name === 'view' || operation.name === 'edit') { + return getTagById(Number(requestParameters.id)); + } - return Promise.resolve(null); - }), + return Promise.resolve(null); + } + ), - stateProcessor: new CallbackStateProcessor(async (data: StateProcessorInput, operation: CrudOperation, requestParameters: RequestParameters) => { - if (operation.name === 'new' || operation.name === 'edit') { - data.id = parseInt(requestParameters.id || 0, 10); - // TODO FIXME : remove this and use proper entity injection! - const tag_rule = Tag.fromJson(data); + stateProcessor: new CallbackStateProcessor( + async ( + data: StateProcessorInput, + operation: CrudOperation, + requestParameters: RequestParameters + ) => { + if (operation.name === 'new' || operation.name === 'edit') { + if (!data) { + throw new Error('Cannot create new object: empty data.'); + } + if (Array.isArray(data)) { + throw new Error('Cannot update data as array for this action.'); + } - if (operation.name === 'new') { - await createTag(tag_rule); - } - if (operation.name === 'edit') { - await updateTag(tag_rule); - } + data.id = Number(requestParameters.id || 0); + // TODO FIXME : remove this and use proper entity injection! + const tag_rule = Tag.fromJson(data); - success('Success!'); - await goto('/crud/tags/list'); - return; - } - }) + if (operation.name === 'new') { + await createTag(tag_rule); + } + if (operation.name === 'edit') { + await updateTag(tag_rule); + } + + success('Success!'); + await goto('/crud/tags/list'); + return; + } + } + ) }); diff --git a/src/lib/crud/cruds/TriageCrud.ts b/src/lib/crud/cruds/TriageCrud.ts index 6ce4255..0186e96 100644 --- a/src/lib/crud/cruds/TriageCrud.ts +++ b/src/lib/crud/cruds/TriageCrud.ts @@ -1,112 +1,135 @@ import { - CallbackStateProcessor, - CallbackStateProvider, - CrudDefinition, - DateField, - Delete, - Edit, - List, - NumberField, - PaginatedResults, - TextField, - UrlAction, - View -} from "@orbitale/svelte-admin"; -import type {CrudOperation} from "@orbitale/svelte-admin/dist/Crud/Operations"; -import type {RequestParameters} from "@orbitale/svelte-admin/dist/request"; -import type {StateProcessorInput} from "@orbitale/svelte-admin/dist/State/Processor"; + CallbackStateProcessor, + CallbackStateProvider, + CrudDefinition, + DateField, + Delete, + Edit, + List, + NumberField, + PaginatedResults, + TextField, + UrlAction, + View, + type CrudOperation, + type RequestParameters, + type StateProcessorInput, + type ListOperationOptions +} from '@orbitale/svelte-admin'; import { - deleteOperation, - getOperationById, - getTriageOperations, - getTriageOperationsCount, updateOperationDetails -} from "$lib/db/operations"; -import type Operation from "$lib/entities/Operation"; -import {success} from "$lib/utils/message"; -import {goto} from "$app/navigation"; + deleteOperation, + getOperationById, + getTriageOperations, + getTriageOperationsCount, + updateOperationDetails +} from '$lib/db/operations'; +import type Operation from '$lib/entities/Operation'; +import { success } from '$lib/utils/message'; +import { goto } from '$app/navigation'; export default new CrudDefinition({ - name: 'triage', - defaultOperationName: "list", - label: {plural: "Triaged operations", singular: "Triaged operation"}, - // minStateLoadingTimeMs: 0, + name: 'triage', + defaultOperationName: 'list', + label: { plural: 'Triaged operations', singular: 'Triaged operation' }, + // minStateLoadingTimeMs: 0, - operations: [ - new List( - [ - new DateField('operation_date', 'Date'), - new TextField('op_type', 'Type 1'), - new TextField('type_display', 'Type 2'), - new TextField('details', 'Details'), - new NumberField('amount_display', 'Montant'), - ], - [ - new UrlAction('View', '/crud/triage/view'), - new UrlAction('Edit', '/crud/triage/edit'), - new UrlAction('Delete', '/crud/triage/delete') - ], - { - pagination: { - enabled: true, - itemsPerPage: 20, - } - }), - new Delete([], new UrlAction('', '/crud/triage/list')), - new Edit([ - new TextField('details', 'Details'), - ]), - new View([ - new TextField('id', 'ID'), - new DateField('operation_date', 'Date'), - new TextField('op_type', 'Type 1'), - new TextField('type_display', 'Type 2'), - new TextField('details', 'Details'), - new NumberField('amount_in_cents', 'Montant (in cents)'), - new NumberField('amount', 'Montant'), - new NumberField('hash', 'Hash'), - new TextField('bank_account', 'Bank account'), - ]), - ], + operations: [ + new List( + [ + new DateField('operation_date', 'Date'), + new TextField('op_type', 'Type 1'), + new TextField('type_display', 'Type 2'), + new TextField('details', 'Details'), + new NumberField('amount_display', 'Montant') + ], + [ + new UrlAction('View', '/crud/triage/view'), + new UrlAction('Edit', '/crud/triage/edit'), + new UrlAction('Delete', '/crud/triage/delete') + ], + { + pagination: { + enabled: true, + itemsPerPage: 20 + } + } + ), + new Delete([], new UrlAction('', '/crud/triage/list')), + new Edit([new TextField('details', 'Details')]), + new View([ + new TextField('id', 'ID'), + new DateField('operation_date', 'Date'), + new TextField('op_type', 'Type 1'), + new TextField('type_display', 'Type 2'), + new TextField('details', 'Details'), + new NumberField('amount_in_cents', 'Montant (in cents)'), + new NumberField('amount', 'Montant'), + new NumberField('hash', 'Hash'), + new TextField('bank_account', 'Bank account') + ]) + ], - stateProvider: new CallbackStateProvider(async (operation: CrudOperation, requestParameters: RequestParameters) => { - if (typeof window === 'undefined') { - // SSR, can't call Tauri API then. - return Promise.resolve([]); - } + stateProvider: new CallbackStateProvider( + async (operation: CrudOperation, requestParameters: RequestParameters) => { + if (typeof window === 'undefined') { + // SSR, can't call Tauri API then. + return Promise.resolve([]); + } - if (operation.name === 'list') { - const results = await getTriageOperations(requestParameters.page||1); - const numberOfItems = await getTriageOperationsCount(); - return Promise.resolve(new PaginatedResults(requestParameters.page, numberOfItems / operation.options.pagination.itemsPerPage, numberOfItems, results)); - } + if (operation.name === 'list') { + const options: ListOperationOptions = operation.options; + const results = await getTriageOperations(Number(requestParameters.page || 1)); + const numberOfItems = await getTriageOperationsCount(); + return Promise.resolve( + new PaginatedResults( + Number(requestParameters.page), + numberOfItems / Number(options.pagination?.itemsPerPage || 10), + numberOfItems, + results + ) + ); + } - if (operation.name === 'view' || operation.name === 'edit') { - return getOperationById(requestParameters.id); - } + if (operation.name === 'view' || operation.name === 'edit') { + return getOperationById(Number(requestParameters.id)); + } - return Promise.resolve(null); - }), - stateProcessor: new CallbackStateProcessor(async (data: StateProcessorInput, operation: CrudOperation, requestParameters: RequestParameters) => { + return Promise.resolve(null); + } + ), + stateProcessor: new CallbackStateProcessor( + async ( + data: StateProcessorInput, + operation: CrudOperation, + requestParameters: RequestParameters + ) => { + if (operation.name === 'edit' || operation.name === 'delete') { + if (!data) { + throw new Error('Cannot create new object: empty data.'); + } + if (Array.isArray(data)) { + throw new Error('Cannot update data as array for this action.'); + } - if (operation.name === 'edit' || operation.name === 'delete') { - const operationObject = await getOperationById(requestParameters.id); - if (!operationObject) { - throw new Error(`Could not find operation with id "${requestParameters.id}".`); - } + const operationObject = await getOperationById(Number(requestParameters.id)); + if (!operationObject) { + throw new Error(`Could not find operation with id "${requestParameters.id}".`); + } - if (operation.name === 'edit') { - operationObject.details = data.details; - await updateOperationDetails(operationObject); - } + if (operation.name === 'edit') { + operationObject.details = data?.details || ''; + await updateOperationDetails(operationObject); + } - if (operation.name === 'delete') { - await deleteOperation(operationObject); - } + if (operation.name === 'delete') { + await deleteOperation(operationObject); + } - success('Success!'); - await goto('/crud/triage/list'); - return; - } - }) + success('Success!'); + await goto('/crud/triage/list'); + return; + } + } + ) }); diff --git a/src/lib/db/bank_accounts.ts b/src/lib/db/bank_accounts.ts index abc7412..a1486c7 100644 --- a/src/lib/db/bank_accounts.ts +++ b/src/lib/db/bank_accounts.ts @@ -2,8 +2,8 @@ import BankAccount from '$lib/entities/BankAccount'; import api_call from '$lib/utils/api_call'; import { writable } from 'svelte/store'; import type { Writable } from 'svelte/store'; -import DeserializedOperation from "$lib/db/operations"; -import BankAccountsCrud from "$lib/crud/cruds/BankAccountsCrud"; +import DeserializedOperation from '$lib/db/operations'; +import BankAccountsCrud from '$lib/crud/cruds/BankAccountsCrud'; export const bankAccountsStore: Writable = writable(); diff --git a/src/lib/db/operations.ts b/src/lib/db/operations.ts index 3ec2815..1824315 100644 --- a/src/lib/db/operations.ts +++ b/src/lib/db/operations.ts @@ -1,10 +1,10 @@ -import Operation, {OperationState} from '$lib/entities/Operation'; +import Operation, { OperationState } from '$lib/entities/Operation'; import api_call from '$lib/utils/api_call'; -import {getTagsByIds} from './tags'; -import {getBankAccountById} from './bank_accounts'; +import { getTagsByIds } from './tags'; +import { getBankAccountById } from './bank_accounts'; import type Tag from '$lib/entities/Tag'; import type SortableField from '$lib/admin/src/SortableField'; -import {OrderBy, orderByToString} from '$lib/admin/src/OrderBy'; +import { OrderBy, orderByToString } from '$lib/admin/src/OrderBy'; import type FilterWithValue from '$lib/admin/src/FilterWithValue'; import type SavedFilter from '$lib/admin/src/SavedFilter'; @@ -27,7 +27,7 @@ export async function getOperations( sortableField: SortableField | null = null, filters: Array | null = null ): Promise> { - const params: {[key: string]: number|string|Array} = { page }; + const params: { [key: string]: number | string | Array } = { page }; if (sortableField) { params['orderField'] = sortableField.property_name; @@ -141,7 +141,9 @@ async function normalizeOperationFromDeserialized( const bank_account = await getBankAccountById(deserialized_operation.bank_account_id); if (!bank_account) { - throw new Error(`Backend could not find bank account with id "${deserialized_operation.bank_account_id}".`); + throw new Error( + `Backend could not find bank account with id "${deserialized_operation.bank_account_id}".` + ); } const tags = await getTagsByIds(deserialized_operation.tags_ids); diff --git a/src/lib/entities/Operation.ts b/src/lib/entities/Operation.ts index c7d7a41..91f3c82 100644 --- a/src/lib/entities/Operation.ts +++ b/src/lib/entities/Operation.ts @@ -3,25 +3,22 @@ import sha512 from '$lib/utils/sha512'; import { DateFormat, dateFormatToRegex, DateTime, NormalizedDate } from '$lib/utils/date'; import type Tag from './Tag'; import type Entity from '$lib/struct/Entity'; -import ConfigFilter from '../admin/src/ConfigFilter'; -import FilterType from '../admin/src/FilterType'; -import { getBankAccountsAsChoices } from '../db/bank_accounts'; export enum OperationState { ok = 'ok', pending_triage = 'pending_triage' } -export const operations_filters = [ - new ConfigFilter('details', 'Details', FilterType.text), - new ConfigFilter('operation_date', 'Date', FilterType.date), - new ConfigFilter('amount_in_cents', 'Amount', FilterType.number), - new ConfigFilter('bank_account_id', 'Bank account', FilterType.entity, { - entities: getBankAccountsAsChoices - }), - new ConfigFilter('tags_names', 'Tags', FilterType.text), - new ConfigFilter('without_tags', 'Without tags', FilterType.boolean) -]; +// export const operations_filters = [ +// new ConfigFilter('details', 'Details', FilterType.text), +// new ConfigFilter('operation_date', 'Date', FilterType.date), +// new ConfigFilter('amount_in_cents', 'Amount', FilterType.number), +// new ConfigFilter('bank_account_id', 'Bank account', FilterType.entity, { +// entities: getBankAccountsAsChoices +// }), +// new ConfigFilter('tags_names', 'Tags', FilterType.text), +// new ConfigFilter('without_tags', 'Without tags', FilterType.boolean) +// ]; export default class Operation implements Entity { public readonly id!: number; @@ -103,7 +100,12 @@ export default class Operation implements Entity { } const parsedDate = Date.parse( - matches.groups?.year + '-' + matches.groups?.month + '-' + matches.groups?.day + 'T00:00:00.000Z' + matches.groups?.year + + '-' + + matches.groups?.month + + '-' + + matches.groups?.day + + 'T00:00:00.000Z' ); if (isNaN(parsedDate)) { throw new Error( diff --git a/src/lib/entities/Tag.ts b/src/lib/entities/Tag.ts index fe41348..b0496bb 100644 --- a/src/lib/entities/Tag.ts +++ b/src/lib/entities/Tag.ts @@ -37,7 +37,7 @@ export default class Tag implements Entity { public static fromJson(json: { name?: string }): Tag { if (json.name === undefined) { - throw new Error('Invaild JSON to create a Tag object: '+JSON.stringify(json)); + throw new Error('Invaild JSON to create a Tag object: ' + JSON.stringify(json)); } return new Tag(0, json.name); diff --git a/src/lib/entities/TagRule.ts b/src/lib/entities/TagRule.ts index 511732a..439ba38 100644 --- a/src/lib/entities/TagRule.ts +++ b/src/lib/entities/TagRule.ts @@ -2,9 +2,9 @@ import type Tag from './Tag'; import type Entity from '$lib/struct/Entity'; export type PartialTagRule = { - tags?: Array - matching_pattern?: string, - is_regex?: string|boolean + tags?: Array; + matching_pattern?: string; + is_regex?: string | boolean; }; export default class TagRule implements Entity { @@ -39,8 +39,14 @@ export default class TagRule implements Entity { } public static fromJson(json: PartialTagRule): TagRule { - if (!Array.isArray(json.tags) || json.matching_pattern === '' || json.matching_pattern === undefined || json.is_regex === '' || json.is_regex === undefined) { - throw new Error('Invaild JSON to create a TagRule object: '+JSON.stringify(json)); + if ( + !Array.isArray(json.tags) || + json.matching_pattern === '' || + json.matching_pattern === undefined || + json.is_regex === '' || + json.is_regex === undefined + ) { + throw new Error('Invaild JSON to create a TagRule object: ' + JSON.stringify(json)); } return new TagRule(0, json.tags, json.matching_pattern, !!json.is_regex); } diff --git a/src/routes/(main)/+layout.svelte b/src/routes/(main)/+layout.svelte index 8d97993..c7abdad 100644 --- a/src/routes/(main)/+layout.svelte +++ b/src/routes/(main)/+layout.svelte @@ -1,7 +1,7 @@ - - + + diff --git a/src/routes/(main)/+page.svelte b/src/routes/(main)/+page.svelte index 90f7c26..ece85e0 100644 --- a/src/routes/(main)/+page.svelte +++ b/src/routes/(main)/+page.svelte @@ -2,7 +2,7 @@ import { dashboard } from '$lib/crud/Dashboard'; - +

{dashboard.adminConfig.head.brandName}

    diff --git a/src/routes/(main)/import/+page.svelte b/src/routes/(main)/import/+page.svelte index dc56456..5bf1617 100644 --- a/src/routes/(main)/import/+page.svelte +++ b/src/routes/(main)/import/+page.svelte @@ -1,5 +1,5 @@

    Import

    diff --git a/src/routes/(main)/import/csv/+page.svelte b/src/routes/(main)/import/csv/+page.svelte index 6a6ad9e..3f139cd 100644 --- a/src/routes/(main)/import/csv/+page.svelte +++ b/src/routes/(main)/import/csv/+page.svelte @@ -7,13 +7,13 @@ import Select from 'carbon-components-svelte/src/Select/Select.svelte'; import SelectItem from 'carbon-components-svelte/src/Select/SelectItem.svelte'; import NumberInput from 'carbon-components-svelte/src/NumberInput/NumberInput.svelte'; - import Table from "carbon-components-svelte/src/DataTable/Table.svelte"; - import TableBody from "carbon-components-svelte/src/DataTable/TableBody.svelte"; - import TableCell from "carbon-components-svelte/src/DataTable/TableCell.svelte"; - import TableContainer from "carbon-components-svelte/src/DataTable/TableContainer.svelte"; - import TableHead from "carbon-components-svelte/src/DataTable/TableHead.svelte"; - import TableHeader from "carbon-components-svelte/src/DataTable/TableHeader.svelte"; - import TableRow from "carbon-components-svelte/src/DataTable/TableRow.svelte"; + import Table from 'carbon-components-svelte/src/DataTable/Table.svelte'; + import TableBody from 'carbon-components-svelte/src/DataTable/TableBody.svelte'; + import TableCell from 'carbon-components-svelte/src/DataTable/TableCell.svelte'; + import TableContainer from 'carbon-components-svelte/src/DataTable/TableContainer.svelte'; + import TableHead from 'carbon-components-svelte/src/DataTable/TableHead.svelte'; + import TableHeader from 'carbon-components-svelte/src/DataTable/TableHeader.svelte'; + import TableRow from 'carbon-components-svelte/src/DataTable/TableRow.svelte'; import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte'; @@ -23,9 +23,9 @@ import Operation, { OperationState } from '$lib/entities/Operation'; import { CsvFieldReference, referenceToEntityProperty } from '$lib/utils/csv'; import { DateFormat } from '$lib/utils/date'; - import type BankAccount from "$lib/entities/BankAccount"; - import {onMount} from "svelte"; - import {getBankAccounts} from "$lib/db/bank_accounts"; + import type BankAccount from '$lib/entities/BankAccount'; + import { onMount } from 'svelte'; + import { getBankAccounts } from '$lib/db/bank_accounts'; let file: File | null = null; let fileContent: string | null = null; @@ -49,7 +49,7 @@ let delimiter: string = '"'; let escapeCharacter: string = '\\'; let dateFormat: DateFormat = DateFormat.DMY_SLASH; - let bankAccountId: string|number|undefined; + let bankAccountId: string | number | undefined; let loading = false; @@ -95,7 +95,7 @@ } async function readCsvFile(reader: FileReader) { - fileContent = (reader.result||'').toString().trim(); + fileContent = (reader.result || '').toString().trim(); const firstLine = fileContent.split('\n')[0] || null; if (!firstLine) { @@ -192,7 +192,9 @@ throw new Error('Bank account is not set.\nPlease set the bank account in the proper field.'); } - const bankAccount = bankAccounts.filter((acc: BankAccount) => acc.id.toString() === bankAccountId?.toString())[0] + const bankAccount = bankAccounts.filter( + (acc: BankAccount) => acc.id.toString() === bankAccountId?.toString() + )[0]; if (!bankAccount) { throw new Error(`Invalid bank account id "${bankAccountId}".`); } @@ -328,11 +330,7 @@ (Remember to refresh on any change) - @@ -347,9 +345,7 @@

    CSV parameters

    - + - + - +
    {#each previewOperations as line, key} - + {key} {#if key < numberOfLinesToRemove} diff --git a/src/routes/(svelteadmin)/+layout.svelte b/src/routes/(svelteadmin)/+layout.svelte index 13e0e91..4fa864c 100644 --- a/src/routes/(svelteadmin)/+layout.svelte +++ b/src/routes/(svelteadmin)/+layout.svelte @@ -1 +1 @@ - + diff --git a/src/routes/(svelteadmin)/crud/[crud]/[operation]/+page.svelte b/src/routes/(svelteadmin)/crud/[crud]/[operation]/+page.svelte index 6472f49..f9f7a22 100644 --- a/src/routes/(svelteadmin)/crud/[crud]/[operation]/+page.svelte +++ b/src/routes/(svelteadmin)/crud/[crud]/[operation]/+page.svelte @@ -13,11 +13,11 @@
    URL: {$page.url}
    {#key $page} - + {/key} diff --git a/svelte.config.js b/svelte.config.js index be9bd49..944ec48 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -12,13 +12,13 @@ const config = { // If your environment is not supported or you settled on a specific environment, switch out the adapter. // See https://kit.svelte.dev/docs/adapters for more information about adapters. adapter: adapter({ - pages: "src-tauri/target/frontend-build", - assets: "src-tauri/target/frontend-build", + pages: 'src-tauri/target/frontend-build', + assets: 'src-tauri/target/frontend-build', precompress: true, fallback: 'index.html', prerender: { - crawl: true, - }, + crawl: true + } }) } }; diff --git a/vite.config.ts b/vite.config.ts index 29ca761..55c51b2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,5 +5,5 @@ export default defineConfig({ plugins: [sveltekit()], server: { //hmr: false, - }, + } });