@cratis/scene.blueprint.invoicing 0.0.0-stage → 4.78.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -2
- package/blueprint.ts +16 -0
- package/blueprintCatalog.json +132 -0
- package/dist/cjs/blueprint.js +19 -0
- package/dist/cjs/blueprint.js.map +1 -0
- package/dist/cjs/index.js +69 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/invoicingApplication.js +49 -0
- package/dist/cjs/invoicingApplication.js.map +1 -0
- package/dist/cjs/invoicingParts.js +151 -0
- package/dist/cjs/invoicingParts.js.map +1 -0
- package/dist/cjs/invoicingQueries.js +66 -0
- package/dist/cjs/invoicingQueries.js.map +1 -0
- package/dist/cjs/invoicingScreenSet.js +47 -0
- package/dist/cjs/invoicingScreenSet.js.map +1 -0
- package/dist/cjs/manifest.js +87 -0
- package/dist/cjs/manifest.js.map +1 -0
- package/dist/cjs/navigation.js +117 -0
- package/dist/cjs/navigation.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/packageName.js +8 -0
- package/dist/cjs/packageName.js.map +1 -0
- package/dist/cjs/screens/index.js +22 -0
- package/dist/cjs/screens/index.js.map +1 -0
- package/dist/cjs/templateProvenance.js +24 -0
- package/dist/cjs/templateProvenance.js.map +1 -0
- package/dist/cjs/templates/calendarTemplate.js +109 -0
- package/dist/cjs/templates/calendarTemplate.js.map +1 -0
- package/dist/cjs/templates/dashboardTemplates.js +153 -0
- package/dist/cjs/templates/dashboardTemplates.js.map +1 -0
- package/dist/cjs/templates/formTemplates.js +51 -0
- package/dist/cjs/templates/formTemplates.js.map +1 -0
- package/dist/cjs/templates/index.js +27 -0
- package/dist/cjs/templates/index.js.map +1 -0
- package/dist/cjs/templates/listTemplates.js +166 -0
- package/dist/cjs/templates/listTemplates.js.map +1 -0
- package/dist/cjs/templates/recordTemplates.js +144 -0
- package/dist/cjs/templates/recordTemplates.js.map +1 -0
- package/dist/esm/blueprint.d.ts +3 -0
- package/dist/esm/blueprint.d.ts.map +1 -0
- package/dist/esm/blueprint.js +17 -0
- package/dist/esm/blueprint.js.map +1 -0
- package/dist/esm/index.d.ts +12 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/invoicingApplication.d.ts +10 -0
- package/dist/esm/invoicingApplication.d.ts.map +1 -0
- package/dist/esm/invoicingApplication.js +44 -0
- package/dist/esm/invoicingApplication.js.map +1 -0
- package/dist/esm/invoicingParts.d.ts +16 -0
- package/dist/esm/invoicingParts.d.ts.map +1 -0
- package/dist/esm/invoicingParts.js +133 -0
- package/dist/esm/invoicingParts.js.map +1 -0
- package/dist/esm/invoicingQueries.d.ts +39 -0
- package/dist/esm/invoicingQueries.d.ts.map +1 -0
- package/dist/esm/invoicingQueries.js +58 -0
- package/dist/esm/invoicingQueries.js.map +1 -0
- package/dist/esm/invoicingScreenSet.d.ts +7 -0
- package/dist/esm/invoicingScreenSet.d.ts.map +1 -0
- package/dist/esm/invoicingScreenSet.js +45 -0
- package/dist/esm/invoicingScreenSet.js.map +1 -0
- package/dist/esm/manifest.d.ts +3 -0
- package/dist/esm/manifest.d.ts.map +1 -0
- package/dist/esm/manifest.js +85 -0
- package/dist/esm/manifest.js.map +1 -0
- package/dist/esm/navigation.d.ts +5 -0
- package/dist/esm/navigation.d.ts.map +1 -0
- package/dist/esm/navigation.js +113 -0
- package/dist/esm/navigation.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/packageName.d.ts +2 -0
- package/dist/esm/packageName.d.ts.map +1 -0
- package/dist/esm/packageName.js +6 -0
- package/dist/esm/packageName.js.map +1 -0
- package/dist/esm/screens/index.d.ts +3 -0
- package/dist/esm/screens/index.d.ts.map +1 -0
- package/dist/esm/screens/index.js +20 -0
- package/dist/esm/screens/index.js.map +1 -0
- package/dist/esm/templateProvenance.d.ts +3 -0
- package/dist/esm/templateProvenance.d.ts.map +1 -0
- package/dist/esm/templateProvenance.js +22 -0
- package/dist/esm/templateProvenance.js.map +1 -0
- package/dist/esm/templates/calendarTemplate.d.ts +3 -0
- package/dist/esm/templates/calendarTemplate.d.ts.map +1 -0
- package/dist/esm/templates/calendarTemplate.js +107 -0
- package/dist/esm/templates/calendarTemplate.js.map +1 -0
- package/dist/esm/templates/dashboardTemplates.d.ts +3 -0
- package/dist/esm/templates/dashboardTemplates.d.ts.map +1 -0
- package/dist/esm/templates/dashboardTemplates.js +151 -0
- package/dist/esm/templates/dashboardTemplates.js.map +1 -0
- package/dist/esm/templates/formTemplates.d.ts +3 -0
- package/dist/esm/templates/formTemplates.d.ts.map +1 -0
- package/dist/esm/templates/formTemplates.js +49 -0
- package/dist/esm/templates/formTemplates.js.map +1 -0
- package/dist/esm/templates/index.d.ts +8 -0
- package/dist/esm/templates/index.d.ts.map +1 -0
- package/dist/esm/templates/index.js +18 -0
- package/dist/esm/templates/index.js.map +1 -0
- package/dist/esm/templates/listTemplates.d.ts +4 -0
- package/dist/esm/templates/listTemplates.d.ts.map +1 -0
- package/dist/esm/templates/listTemplates.js +163 -0
- package/dist/esm/templates/listTemplates.js.map +1 -0
- package/dist/esm/templates/recordTemplates.d.ts +3 -0
- package/dist/esm/templates/recordTemplates.d.ts.map +1 -0
- package/dist/esm/templates/recordTemplates.js +142 -0
- package/dist/esm/templates/recordTemplates.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -0
- package/index.ts +14 -0
- package/invoicingApplication.ts +28 -0
- package/invoicingParts.ts +58 -0
- package/invoicingQueries.ts +58 -0
- package/invoicingScreenSet.ts +22 -0
- package/manifest.ts +41 -0
- package/navigation.ts +54 -0
- package/package.json +78 -4
- package/packageName.ts +5 -0
- package/sampleData/invoicing-en.json +988 -0
- package/screens/index.ts +20 -0
- package/templateProvenance.ts +15 -0
- package/templates/calendarTemplate.ts +57 -0
- package/templates/dashboardTemplates.ts +76 -0
- package/templates/formTemplates.ts +34 -0
- package/templates/index.ts +18 -0
- package/templates/listTemplates.ts +106 -0
- package/templates/recordTemplates.ts +79 -0
- package/thumbnails/InvoicePage.svg +17 -0
- package/thumbnails/InvoicesList.svg +29 -0
- package/thumbnails/InvoicingDueDates.svg +34 -0
- package/thumbnails/InvoicingOverview.svg +15 -0
- package/thumbnails/InvoicingQuickCreateInvoice.svg +12 -0
- package/thumbnails/InvoicingRecordPayment.svg +12 -0
- package/thumbnails/PaymentsList.svg +29 -0
- package/thumbnails/invoicing.svg +14 -0
package/screens/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { Screen } from '@cratis/scene.model';
|
|
5
|
+
import { LayoutName } from '@cratis/scene.blueprint.default';
|
|
6
|
+
import { invoicingScreenDefinitions } from '../invoicingScreenSet';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The ready-to-use screens: real `Screen` instances, one for each entry of the manifest's `blueprint.screenSet`, each filling the screen template it names in
|
|
10
|
+
* the foundation's application shell. A new application that adopts the blueprint gets these, wired into its navigation (`invoicingNavigation`).
|
|
11
|
+
*/
|
|
12
|
+
export const invoicingScreens: Screen[] = invoicingScreenDefinitions.map(definition => ({
|
|
13
|
+
name: definition.name,
|
|
14
|
+
layout: LayoutName.AppShell,
|
|
15
|
+
screenTemplate: definition.template,
|
|
16
|
+
slotContent: {},
|
|
17
|
+
forms: [],
|
|
18
|
+
contributions: [],
|
|
19
|
+
path: definition.name.split('.'),
|
|
20
|
+
}));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { TemplateMetadata } from '@cratis/scene.model';
|
|
5
|
+
|
|
6
|
+
/** The compatibility, attribution and license metadata every template this blueprint ships carries. */
|
|
7
|
+
export const invoicingTemplateProvenance: Pick<TemplateMetadata, 'compatibility' | 'attribution' | 'license' | 'licenseUrl'> = {
|
|
8
|
+
compatibility: {
|
|
9
|
+
scene: '^4.13.0',
|
|
10
|
+
packages: [{ name: 'Cratis.Blueprint.Default', versionRange: '^1.0.0' }],
|
|
11
|
+
},
|
|
12
|
+
attribution: { author: 'Cratis', url: 'https://github.com/Cratis/Scene/tree/main/Source/JavaScript/blueprint.invoicing' },
|
|
13
|
+
license: 'MIT',
|
|
14
|
+
licenseUrl: 'https://github.com/Cratis/Scene/blob/main/LICENSE',
|
|
15
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { ScreenTemplate, TemplateScope, WidthSizeClass } from '@cratis/scene.model';
|
|
5
|
+
import { column, grid, slotLeaf } from '@cratis/scene.blueprint.default';
|
|
6
|
+
import { InvoicingQuery, fromQuery, localeOfData } from '../invoicingQueries';
|
|
7
|
+
import { actionButton, component, goesTo, invoicingMetadata, opensDialog, withBehaviors } from '../invoicingParts';
|
|
8
|
+
|
|
9
|
+
interface Action { id: string; label: string; emphasis: 'primary' | 'secondary' | 'quiet' | 'danger'; dialog?: string; screen?: string }
|
|
10
|
+
|
|
11
|
+
interface CalendarOptions {
|
|
12
|
+
name: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
description: string;
|
|
15
|
+
title: string;
|
|
16
|
+
subtitle: string;
|
|
17
|
+
label: string;
|
|
18
|
+
listLabel: string;
|
|
19
|
+
events: string;
|
|
20
|
+
columns: string;
|
|
21
|
+
titleField: string;
|
|
22
|
+
actions: Action[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* One calendar: the foundation's `calendarGrid` on a month, a week or as an agenda. The status is told in words as well as by color, the day and view are kept in the
|
|
27
|
+
* address so a link shares the calendar as it is, and the list beside it names everything in the period.
|
|
28
|
+
*/
|
|
29
|
+
function calendarOf(options: CalendarOptions): ScreenTemplate {
|
|
30
|
+
const id = options.name;
|
|
31
|
+
return {
|
|
32
|
+
name: options.name,
|
|
33
|
+
fitsSlot: 'content',
|
|
34
|
+
slots: [{ name: 'header' }, { name: 'calendar' }, { name: 'due' }],
|
|
35
|
+
arrangement: {
|
|
36
|
+
root: column([slotLeaf('header'), grid([slotLeaf('calendar', { span: 2 }), slotLeaf('due')], 3, 16)], 16),
|
|
37
|
+
overrides: [{ width: WidthSizeClass.Compact, root: column([slotLeaf('header'), slotLeaf('calendar'), slotLeaf('due')], 16) }],
|
|
38
|
+
},
|
|
39
|
+
content: {
|
|
40
|
+
header: [component(`${id}-header`, 'pageHeader', { title: options.title, subtitle: options.subtitle }, {
|
|
41
|
+
actions: options.actions.map(action => withBehaviors(actionButton(`${id}-${action.id}`, action.label, action.emphasis), action.dialog ? opensDialog(action.dialog) : goesTo(action.screen!))),
|
|
42
|
+
})],
|
|
43
|
+
calendar: [component(`${id}-grid`, 'calendarGrid', { label: options.label, events: fromQuery(options.events), date: fromQuery(InvoicingQuery.Settings, '0.today'), urlKey: 'day', ...localeOfData })],
|
|
44
|
+
due: [component(`${id}-list`, 'dataList', { label: options.listLabel, rows: fromQuery(options.events), columns: fromQuery(options.columns), titleField: options.titleField, pageSize: 6, ...localeOfData })],
|
|
45
|
+
},
|
|
46
|
+
displayName: options.displayName,
|
|
47
|
+
description: options.description,
|
|
48
|
+
metadata: invoicingMetadata(options.name, 'Calendar', [TemplateScope.Module, TemplateScope.Feature]),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The due-date calendar: the day each invoice falls due on a month, a week or an agenda; the status is told in words as well as by color, and the list beside it names every invoice due in the period. */
|
|
53
|
+
export const invoicingDueDatesTemplate = calendarOf({
|
|
54
|
+
name: 'InvoicingDueDates', displayName: 'Invoicing due-date calendar', description: 'The day each invoice falls due, on a month, a week or as an agenda, with the status told in words.', title: 'Due dates', subtitle: 'When each invoice falls due', label: 'Invoice due dates', listLabel: 'Due in this period',
|
|
55
|
+
events: InvoicingQuery.DueDates, columns: InvoicingQuery.DueColumns, titleField: 'title',
|
|
56
|
+
actions: [{ id: 'invoices', label: 'Open the invoices', emphasis: 'secondary', screen: 'invoicing.list' }],
|
|
57
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { ScreenTemplate, TemplateScope } from '@cratis/scene.model';
|
|
5
|
+
import { dashboardScreenTemplate } from '@cratis/scene.blueprint.default';
|
|
6
|
+
import { InvoicingEntityKind, InvoicingQuery, currencyOfData, fromQuery, localeOfData, invoicingQueriesOf } from '../invoicingQueries';
|
|
7
|
+
import { actionButton, component, goesTo, invoicingMetadata, opensDialog, withBehaviors } from '../invoicingParts';
|
|
8
|
+
|
|
9
|
+
interface KeyFigure { id: string; label: string; query: string; index: number; format?: 'currency' | 'percent'; fractionDigits?: number; goodDirection?: 'up' | 'down'; comparison: string }
|
|
10
|
+
interface Chart { id: string; title: string; kind: 'bar' | 'line' | 'donut' | 'funnel'; query: string }
|
|
11
|
+
interface Action { id: string; label: string; emphasis: 'primary' | 'secondary' | 'quiet' | 'danger'; dialog?: string; screen?: string }
|
|
12
|
+
|
|
13
|
+
interface DashboardOptions {
|
|
14
|
+
name: string;
|
|
15
|
+
displayName: string;
|
|
16
|
+
description: string;
|
|
17
|
+
title: string;
|
|
18
|
+
subtitle: string;
|
|
19
|
+
figures: KeyFigure[];
|
|
20
|
+
charts: Chart[];
|
|
21
|
+
table: { label: string; rows: string; columns: string; titleField: string; pageSize: number };
|
|
22
|
+
aside: { kind: 'activity'; label: string; query: string } | { kind: 'checklist'; label: string; query: string };
|
|
23
|
+
actions: Action[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One dashboard: the foundation `DashboardScreen` - key figures over two charts, a table beneath and a list beside - with every region its own component with its own
|
|
28
|
+
* loading, empty and error states, so one failed query leaves the others on the page.
|
|
29
|
+
*/
|
|
30
|
+
function dashboardOf(options: DashboardOptions): ScreenTemplate {
|
|
31
|
+
const id = options.name;
|
|
32
|
+
return {
|
|
33
|
+
name: options.name,
|
|
34
|
+
fitsSlot: 'content',
|
|
35
|
+
slots: dashboardScreenTemplate.slots,
|
|
36
|
+
arrangement: dashboardScreenTemplate.arrangement,
|
|
37
|
+
content: {
|
|
38
|
+
header: [component(`${id}-header`, 'pageHeader', { title: options.title, subtitle: options.subtitle }, {
|
|
39
|
+
actions: options.actions.map(action => withBehaviors(actionButton(`${id}-${action.id}`, action.label, action.emphasis), action.dialog ? opensDialog(action.dialog) : goesTo(action.screen!))),
|
|
40
|
+
})],
|
|
41
|
+
kpis: options.figures.map(figure => component(`${id}-${figure.id}`, 'statCard', {
|
|
42
|
+
label: figure.label, value: fromQuery(figure.query, `${figure.index}.value`), change: fromQuery(figure.query, `${figure.index}.change`), comparison: figure.comparison,
|
|
43
|
+
...(figure.format ? { format: figure.format } : {}), ...(figure.fractionDigits === undefined ? {} : { fractionDigits: figure.fractionDigits }), ...(figure.goodDirection ? { goodDirection: figure.goodDirection } : {}), ...currencyOfData,
|
|
44
|
+
})),
|
|
45
|
+
charts: options.charts.map(chart => component(`${id}-${chart.id}`, 'chartWrapper', {
|
|
46
|
+
title: chart.title, kind: chart.kind, categories: fromQuery(chart.query, '0.categories'), series: fromQuery(chart.query, '0.series'), ...localeOfData,
|
|
47
|
+
})),
|
|
48
|
+
tables: [component(`${id}-table`, 'dataList', {
|
|
49
|
+
label: options.table.label, rows: fromQuery(options.table.rows), columns: fromQuery(options.table.columns), titleField: options.table.titleField, pageSize: options.table.pageSize, ...localeOfData,
|
|
50
|
+
})],
|
|
51
|
+
aside: [options.aside.kind === 'activity'
|
|
52
|
+
? component(`${id}-aside`, 'activityFeed', { label: options.aside.label, entries: fromQuery(options.aside.query), ...localeOfData })
|
|
53
|
+
: component(`${id}-aside`, 'checklistSection', { label: options.aside.label, sections: fromQuery(options.aside.query), ...localeOfData })],
|
|
54
|
+
},
|
|
55
|
+
displayName: options.displayName,
|
|
56
|
+
description: options.description,
|
|
57
|
+
metadata: invoicingMetadata(options.name, 'Dashboard', [TemplateScope.Module, TemplateScope.Feature]),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The receivables overview: three key figures over two charts - receivables by age and invoiced against collected - with the invoices beneath and the latest activity beside them. */
|
|
62
|
+
export const invoicingOverviewTemplate = dashboardOf({
|
|
63
|
+
name: 'InvoicingOverview', displayName: 'Invoicing overview', description: 'Outstanding, overdue and collected this month, receivables by age, invoiced against collected and the invoices to chase.', title: 'Receivables overview', subtitle: 'What is owed and what has come in',
|
|
64
|
+
figures: [
|
|
65
|
+
{ id: 'outstanding', label: 'Outstanding', query: InvoicingQuery.Kpis, index: 0, format: 'currency', goodDirection: 'down', comparison: 'last month' },
|
|
66
|
+
{ id: 'overdue', label: 'Overdue', query: InvoicingQuery.Kpis, index: 1, format: 'currency', goodDirection: 'down', comparison: 'last month' },
|
|
67
|
+
{ id: 'collected', label: 'Collected this month', query: InvoicingQuery.Kpis, index: 2, format: 'currency', comparison: 'last month' },
|
|
68
|
+
],
|
|
69
|
+
charts: [
|
|
70
|
+
{ id: 'aging', title: 'Receivables by age', kind: 'bar', query: InvoicingQuery.Aging },
|
|
71
|
+
{ id: 'flow', title: 'Invoiced and collected', kind: 'line', query: InvoicingQuery.InvoicedAndCollected },
|
|
72
|
+
],
|
|
73
|
+
table: { label: 'Invoices', titleField: 'number', pageSize: 5, rows: invoicingQueriesOf(InvoicingEntityKind.Invoice).rows, columns: invoicingQueriesOf(InvoicingEntityKind.Invoice).columns },
|
|
74
|
+
aside: { kind: 'activity', label: 'Latest activity', query: InvoicingQuery.Activity },
|
|
75
|
+
actions: [{ id: 'new', label: 'New invoice', emphasis: 'primary', dialog: 'InvoicingQuickCreateInvoice' }],
|
|
76
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { DialogTemplate, TemplateScope } from '@cratis/scene.model';
|
|
5
|
+
import { quickCreateDialogTemplate } from '@cratis/scene.blueprint.default';
|
|
6
|
+
import { InvoicingQuery, fromQuery, localeOfData } from '../invoicingQueries';
|
|
7
|
+
import { closesDialog, component, invoicingMetadata, withBehaviors } from '../invoicingParts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* One quick-create dialog: the foundation `QuickCreateDialog` - a short form opened from a page to make one record without leaving it - with the fields
|
|
11
|
+
* that thing needs to get started.
|
|
12
|
+
*/
|
|
13
|
+
function quickCreateOf(name: string, title: string, sections: string, noun: string, submit: string): DialogTemplate {
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
slots: quickCreateDialogTemplate.slots,
|
|
17
|
+
content: {
|
|
18
|
+
header: [component(`${name}-frame`, 'dialogFrame', { title, subtitle: 'Only what is needed to get started' })],
|
|
19
|
+
body: [withBehaviors(component(`${name}-form`, 'formScreenBody', {
|
|
20
|
+
mode: 'create', saveAndNew: false, strings: { formSave: submit }, sections: fromQuery(sections), errors: fromQuery(InvoicingQuery.FormErrors, '0'), ...localeOfData,
|
|
21
|
+
}), closesDialog())],
|
|
22
|
+
actions: [],
|
|
23
|
+
},
|
|
24
|
+
displayName: `Invoicing quick create ${noun}`,
|
|
25
|
+
description: `A short form that creates one ${noun} without leaving the page.`,
|
|
26
|
+
metadata: invoicingMetadata(name, 'Dialog', [TemplateScope.Slice]),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The quick dialogs of the blueprint. */
|
|
31
|
+
export const invoicingDialogs: DialogTemplate[] = [
|
|
32
|
+
quickCreateOf('InvoicingQuickCreateInvoice', 'New invoice', 'InvoiceQuickSections', 'invoice', 'Create'),
|
|
33
|
+
quickCreateOf('InvoicingRecordPayment', 'Record a payment', 'InvoicingRecordPaymentSections', 'payment', 'Record payment'),
|
|
34
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { invoicingOverviewTemplate } from './dashboardTemplates';
|
|
5
|
+
import { invoicesListTemplate, paymentsListTemplate } from './listTemplates';
|
|
6
|
+
import { invoicePageTemplate } from './recordTemplates';
|
|
7
|
+
import { invoicingDueDatesTemplate } from './calendarTemplate';
|
|
8
|
+
|
|
9
|
+
export * from './dashboardTemplates';
|
|
10
|
+
export * from './listTemplates';
|
|
11
|
+
export * from './recordTemplates';
|
|
12
|
+
export * from './calendarTemplate';
|
|
13
|
+
export * from './formTemplates';
|
|
14
|
+
|
|
15
|
+
/** Every screen template this blueprint provides, in the order the manifest lists them. */
|
|
16
|
+
export const invoicingScreenTemplates = [invoicingOverviewTemplate, invoicesListTemplate, invoicePageTemplate, paymentsListTemplate, invoicingDueDatesTemplate];
|
|
17
|
+
|
|
18
|
+
export { invoicingDialogs as invoicingDialogTemplates } from './formTemplates';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { InteractionActionKind, InteractionTriggerKind, NotificationLevel, NotifyAction, ScreenTemplate, TemplateScope } from '@cratis/scene.model';
|
|
5
|
+
import { listScreenTemplate } from '@cratis/scene.blueprint.default';
|
|
6
|
+
import { InvoicingEntityKind, fromQuery, invoicingQueriesOf, localeOfData } from '../invoicingQueries';
|
|
7
|
+
import { actionButton, component, goesTo, invoicingMetadata, opensDialog, opensDialogOnSelect, opensRecordOnSelect, withBehaviors } from '../invoicingParts';
|
|
8
|
+
|
|
9
|
+
const listScopes = [TemplateScope.Module, TemplateScope.Feature];
|
|
10
|
+
|
|
11
|
+
/** The Export button: it tells the user the export has started, which is what the shell does for a long-running action it does not wait on. */
|
|
12
|
+
const exportAction = (id: string) => withBehaviors(actionButton(id, 'Export', 'secondary'), [{
|
|
13
|
+
bindings: [{
|
|
14
|
+
trigger: { kind: InteractionTriggerKind.Click },
|
|
15
|
+
actions: [{ kind: InteractionActionKind.Notify, level: NotificationLevel.Info, message: { text: 'The export has started.' } } as unknown as NotifyAction],
|
|
16
|
+
}],
|
|
17
|
+
}]);
|
|
18
|
+
|
|
19
|
+
interface ListOptions {
|
|
20
|
+
name: string;
|
|
21
|
+
entity: InvoicingEntityKind;
|
|
22
|
+
displayName: string;
|
|
23
|
+
description: string;
|
|
24
|
+
title: string;
|
|
25
|
+
subtitle: string;
|
|
26
|
+
searchLabel: string;
|
|
27
|
+
urlKey: string;
|
|
28
|
+
/** The primary action of the header: a quick-create dialog to open, or a screen to go to. */
|
|
29
|
+
primary: { label: string; dialog?: string; screen?: string };
|
|
30
|
+
rowActions: string[];
|
|
31
|
+
bulkActions: string[];
|
|
32
|
+
titleField: string;
|
|
33
|
+
/** The screen a chosen row opens; the list then selects one row at a time. Absent, rows are selected many at a time for the bulk bar. */
|
|
34
|
+
recordScreen?: string;
|
|
35
|
+
/** The side sheet a chosen row opens instead of a record page; the list then selects one row at a time. */
|
|
36
|
+
recordDialog?: string;
|
|
37
|
+
/** Group the rows (`variant: grouped`) by this field. */
|
|
38
|
+
grouped?: { groupBy: string };
|
|
39
|
+
/** Rows are not selectable at all: a record of what happened, which nobody edits. */
|
|
40
|
+
readOnly?: boolean;
|
|
41
|
+
/** Show the records as cards, each with the picture its `imageField` names. */
|
|
42
|
+
cards?: { imageField?: string };
|
|
43
|
+
/** Tint a row by the tone in this field (always beside a status column that says the same in words). */
|
|
44
|
+
toneField?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* One list: the foundation `ListScreen` with its header, toolbar, filters, list and bulk bar filled for an entity. The slots, arrangement and
|
|
49
|
+
* every behavior - sort, search, paging and selection in the address, the table collapsing to cards on a phone - are the foundation's.
|
|
50
|
+
*/
|
|
51
|
+
function listTemplateOf(options: ListOptions): ScreenTemplate {
|
|
52
|
+
const queries = invoicingQueriesOf(options.entity);
|
|
53
|
+
const id = options.name;
|
|
54
|
+
const listId = `${id}-records`;
|
|
55
|
+
const primary = withBehaviors(actionButton(`${id}-primary`, options.primary.label, 'primary'), options.primary.dialog ? opensDialog(options.primary.dialog) : goesTo(options.primary.screen!));
|
|
56
|
+
const list = component(listId, 'dataList', {
|
|
57
|
+
label: options.title, rows: fromQuery(queries.rows), columns: fromQuery(queries.columns), titleField: options.titleField, pageSize: options.cards ? 12 : 10, urlKey: options.urlKey, ...localeOfData,
|
|
58
|
+
rowActions: options.rowActions,
|
|
59
|
+
...(options.readOnly ? {} : { selectionMode: options.recordScreen || options.recordDialog ? 'single' : 'multiple' }),
|
|
60
|
+
...(options.cards ? { variant: 'cards', ...(options.cards.imageField ? { imageField: options.cards.imageField } : {}) } : {}),
|
|
61
|
+
...(options.grouped ? { variant: 'grouped', groupBy: options.grouped.groupBy } : {}),
|
|
62
|
+
...(options.toneField ? { toneField: options.toneField } : {}),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
name: options.name,
|
|
67
|
+
fitsSlot: 'content',
|
|
68
|
+
slots: listScreenTemplate.slots,
|
|
69
|
+
arrangement: listScreenTemplate.arrangement,
|
|
70
|
+
content: {
|
|
71
|
+
header: [component(`${id}-header`, 'pageHeader', { title: options.title, subtitle: options.subtitle }, { actions: [exportAction(`${id}-export`), primary] })],
|
|
72
|
+
toolbar: [
|
|
73
|
+
component(`${id}-search`, 'globalSearch', { placeholder: options.searchLabel, label: options.searchLabel, urlKey: `${options.urlKey}q` }),
|
|
74
|
+
component(`${id}-views`, 'savedViewsMenu', { views: fromQuery(queries.views), savePermission: 'enabled', managePermission: 'enabled' }),
|
|
75
|
+
component(`${id}-columns`, 'columnChooser', { columns: fromQuery(queries.columnChoices) }),
|
|
76
|
+
],
|
|
77
|
+
filters: [component(`${id}-filter`, 'filterBuilder', { fields: fromQuery(queries.filterFields), ...localeOfData })],
|
|
78
|
+
list: [options.recordScreen ? withBehaviors(list, opensRecordOnSelect(listId, options.recordScreen)) : options.recordDialog ? withBehaviors(list, opensDialogOnSelect(options.recordDialog)) : list],
|
|
79
|
+
bulkBar: options.recordScreen || options.recordDialog || options.readOnly ? [] : [component(`${id}-bulk`, 'bulkBar', { selection: fromQuery(queries.selection), actions: options.bulkActions })],
|
|
80
|
+
footer: [],
|
|
81
|
+
},
|
|
82
|
+
displayName: options.displayName,
|
|
83
|
+
description: options.description,
|
|
84
|
+
metadata: invoicingMetadata(options.name, 'List', listScopes),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/** The invoices: every invoice with its customer, issue and due dates, amount and status. Choosing an invoice opens it. */
|
|
90
|
+
export const invoicesListTemplate = listTemplateOf({
|
|
91
|
+
name: 'InvoicesList',
|
|
92
|
+
entity: InvoicingEntityKind.Invoice, displayName: 'Invoices list', description: 'Every invoice with its customer, issue and due dates, amount and status, filtered by customer, status and date.', title: 'Invoices', subtitle: 'Everything you have billed', searchLabel: 'Search invoices', urlKey: 'invoices',
|
|
93
|
+
primary: { label: 'New invoice', dialog: 'InvoicingQuickCreateInvoice' },
|
|
94
|
+
rowActions: ['Open', 'Send reminder'], bulkActions: [],
|
|
95
|
+
titleField: 'number', recordScreen: 'invoicing.record'
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
/** The payments: what came in, which invoice it settled, when and by what method. A record of what happened, which nobody edits. */
|
|
99
|
+
export const paymentsListTemplate = listTemplateOf({
|
|
100
|
+
name: 'PaymentsList',
|
|
101
|
+
entity: InvoicingEntityKind.Payment, displayName: 'Payments list', description: 'The payments received with the invoice each settled, when and how, filtered by method and date.', title: 'Payments', subtitle: 'What has come in', searchLabel: 'Search payments', urlKey: 'payments',
|
|
102
|
+
primary: { label: 'Open the invoices', screen: 'invoicing.list' },
|
|
103
|
+
rowActions: ['Open invoice'], bulkActions: [],
|
|
104
|
+
titleField: 'reference',
|
|
105
|
+
readOnly: true
|
|
106
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import { ScreenTemplate, TemplateScope } from '@cratis/scene.model';
|
|
5
|
+
import { recordPageTemplate } from '@cratis/scene.blueprint.default';
|
|
6
|
+
import { InvoicingEntityKind, currencyOfData, fromQuery, localeOfData, invoicingQueriesOf } from '../invoicingQueries';
|
|
7
|
+
import { actionButton, component, goesTo, invoicingMetadata, opensDialog, withBehaviors } from '../invoicingParts';
|
|
8
|
+
|
|
9
|
+
interface Fact { id: string; label: string; field: string; format?: 'currency' | 'percent' }
|
|
10
|
+
interface Extra { key: string; label: string; titleField: string; pageSize?: number }
|
|
11
|
+
interface Action { id: string; label: string; emphasis: 'primary' | 'secondary' | 'quiet' | 'danger'; dialog?: string; screen?: string }
|
|
12
|
+
|
|
13
|
+
interface RecordOptions {
|
|
14
|
+
name: string;
|
|
15
|
+
entity: InvoicingEntityKind;
|
|
16
|
+
displayName: string;
|
|
17
|
+
description: string;
|
|
18
|
+
titleField: string;
|
|
19
|
+
backScreen: string;
|
|
20
|
+
backLabel: string;
|
|
21
|
+
facts: Fact[];
|
|
22
|
+
extras: Extra[];
|
|
23
|
+
actions: Action[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* One record page: the foundation `RecordPage` with the record's own title, its status and key facts at the top, its fields and the lists that belong to it in the
|
|
28
|
+
* main region, and the activity beside it. What it shows is the record the list handed it the id of.
|
|
29
|
+
*/
|
|
30
|
+
function recordPageOf(options: RecordOptions): ScreenTemplate {
|
|
31
|
+
const queries = invoicingQueriesOf(options.entity);
|
|
32
|
+
const id = options.name;
|
|
33
|
+
return {
|
|
34
|
+
name: options.name,
|
|
35
|
+
fitsSlot: 'content',
|
|
36
|
+
slots: recordPageTemplate.slots,
|
|
37
|
+
arrangement: recordPageTemplate.arrangement,
|
|
38
|
+
content: {
|
|
39
|
+
header: [component(`${id}-header`, 'pageHeader', { title: fromQuery(queries.record, `0.${options.titleField}`), subtitle: fromQuery(queries.record, '0.summary') }, {
|
|
40
|
+
actions: [
|
|
41
|
+
withBehaviors(actionButton(`${id}-back`, options.backLabel, 'secondary'), goesTo(options.backScreen)),
|
|
42
|
+
...options.actions.map(action => withBehaviors(actionButton(`${id}-${action.id}`, action.label, action.emphasis), action.dialog ? opensDialog(action.dialog) : goesTo(action.screen!))),
|
|
43
|
+
],
|
|
44
|
+
})],
|
|
45
|
+
summary: [
|
|
46
|
+
component(`${id}-status`, 'statusChip', { status: fromQuery(queries.summary, '0.status'), tone: fromQuery(queries.summary, '0.tone') }),
|
|
47
|
+
...options.facts.map(fact => component(`${id}-${fact.id}`, 'statCard', {
|
|
48
|
+
label: fact.label, value: fromQuery(queries.summary, `0.${fact.field}`), ...(fact.format ? { format: fact.format } : {}), ...currencyOfData,
|
|
49
|
+
})),
|
|
50
|
+
],
|
|
51
|
+
tabs: [component(`${id}-tabs`, 'tabSet', { tabs: fromQuery(queries.tabs), urlKey: 'tab', label: `${options.displayName} sections` })],
|
|
52
|
+
main: [
|
|
53
|
+
component(`${id}-form`, 'formScreenBody', { mode: 'readOnly', sections: fromQuery(queries.sections), values: fromQuery(queries.record, '0'), ...currencyOfData }),
|
|
54
|
+
...options.extras.map(extra => component(`${id}-${extra.key.toLowerCase()}`, 'dataList', {
|
|
55
|
+
label: extra.label, rows: fromQuery(`${options.entity}${extra.key}`), columns: fromQuery(`${options.entity}${extra.key}Columns`), titleField: extra.titleField, pageSize: extra.pageSize ?? 8, ...currencyOfData,
|
|
56
|
+
})),
|
|
57
|
+
],
|
|
58
|
+
related: [],
|
|
59
|
+
activity: [component(`${id}-activity`, 'activityFeed', { label: 'Activity', entries: fromQuery(queries.activity), ...localeOfData })],
|
|
60
|
+
aside: [],
|
|
61
|
+
},
|
|
62
|
+
displayName: options.displayName,
|
|
63
|
+
description: options.description,
|
|
64
|
+
metadata: invoicingMetadata(options.name, 'Detail', [TemplateScope.Feature, TemplateScope.Subfeature]),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The invoice page: the invoice number as its title, its status, total, paid amount and balance due at the top, its fields, lines and payments in the main region, and the activity beside it. */
|
|
69
|
+
export const invoicePageTemplate = recordPageOf({
|
|
70
|
+
name: 'InvoicePage', entity: InvoicingEntityKind.Invoice, displayName: 'Invoice page', description: 'One invoice: status, total, paid and balance due, its details, lines and payments, and its activity.', titleField: 'number', backScreen: 'invoicing.list', backLabel: 'Back to invoices',
|
|
71
|
+
facts: [
|
|
72
|
+
{ id: 'total', label: 'Invoice total', field: 'total', format: 'currency' },
|
|
73
|
+
{ id: 'paid', label: 'Paid', field: 'paid', format: 'currency' },
|
|
74
|
+
{ id: 'balance', label: 'Balance due', field: 'balance', format: 'currency' },
|
|
75
|
+
{ id: 'days', label: 'Days to due date', field: 'daysToDue' },
|
|
76
|
+
],
|
|
77
|
+
extras: [{ key: 'Lines', label: 'Invoice lines', titleField: 'description' }, { key: 'Payments', label: 'Payments', titleField: 'reference' }],
|
|
78
|
+
actions: [{ id: 'pay', label: 'Record payment', emphasis: 'primary', dialog: 'InvoicingRecordPayment' }],
|
|
79
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="InvoicePage">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="70" height="30" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
6
|
+
<rect x="160" y="40" width="70" height="30" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
7
|
+
<rect x="236" y="40" width="70" height="30" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
8
|
+
<rect x="84" y="78" width="150" height="110" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
9
|
+
<rect x="242" y="78" width="66" height="110" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
10
|
+
<rect x="94" y="90" width="130" height="6" rx="3" fill="#9aa3b2"/>
|
|
11
|
+
<rect x="94" y="107" width="130" height="6" rx="3" fill="#9aa3b2"/>
|
|
12
|
+
<rect x="94" y="124" width="130" height="6" rx="3" fill="#9aa3b2"/>
|
|
13
|
+
<rect x="94" y="141" width="130" height="6" rx="3" fill="#9aa3b2"/>
|
|
14
|
+
<rect x="250" y="90" width="50" height="6" rx="3" fill="#16a34a"/>
|
|
15
|
+
<rect x="250" y="107" width="50" height="6" rx="3" fill="#16a34a"/>
|
|
16
|
+
<rect x="250" y="124" width="50" height="6" rx="3" fill="#16a34a"/>
|
|
17
|
+
</svg>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="InvoicesList">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
6
|
+
<rect x="88" y="44" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
7
|
+
<rect x="90" y="82" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
8
|
+
<rect x="90" y="94" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
9
|
+
<rect x="160" y="40" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
10
|
+
<rect x="164" y="44" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
11
|
+
<rect x="166" y="82" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
12
|
+
<rect x="166" y="94" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
13
|
+
<rect x="236" y="40" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
14
|
+
<rect x="240" y="44" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
15
|
+
<rect x="242" y="82" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
16
|
+
<rect x="242" y="94" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
17
|
+
<rect x="84" y="116" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
18
|
+
<rect x="88" y="120" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
19
|
+
<rect x="90" y="158" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
20
|
+
<rect x="90" y="170" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
21
|
+
<rect x="160" y="116" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
22
|
+
<rect x="164" y="120" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
23
|
+
<rect x="166" y="158" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
24
|
+
<rect x="166" y="170" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
25
|
+
<rect x="236" y="116" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
26
|
+
<rect x="240" y="120" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
27
|
+
<rect x="242" y="158" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
28
|
+
<rect x="242" y="170" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
29
|
+
</svg>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="InvoicingDueDates">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="224" height="148" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
6
|
+
<rect x="90" y="70" width="26" height="24" rx="4" fill="#16a34a"/>
|
|
7
|
+
<rect x="120" y="70" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
8
|
+
<rect x="150" y="70" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
9
|
+
<rect x="180" y="70" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
10
|
+
<rect x="210" y="70" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
11
|
+
<rect x="240" y="70" width="26" height="24" rx="4" fill="#16a34a"/>
|
|
12
|
+
<rect x="270" y="70" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
13
|
+
<rect x="90" y="98" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
14
|
+
<rect x="120" y="98" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
15
|
+
<rect x="150" y="98" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
16
|
+
<rect x="180" y="98" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
17
|
+
<rect x="210" y="98" width="26" height="24" rx="4" fill="#16a34a"/>
|
|
18
|
+
<rect x="240" y="98" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
19
|
+
<rect x="270" y="98" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
20
|
+
<rect x="90" y="126" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
21
|
+
<rect x="120" y="126" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
22
|
+
<rect x="150" y="126" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
23
|
+
<rect x="180" y="126" width="26" height="24" rx="4" fill="#16a34a"/>
|
|
24
|
+
<rect x="210" y="126" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
25
|
+
<rect x="240" y="126" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
26
|
+
<rect x="270" y="126" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
27
|
+
<rect x="90" y="154" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
28
|
+
<rect x="120" y="154" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
29
|
+
<rect x="150" y="154" width="26" height="24" rx="4" fill="#16a34a"/>
|
|
30
|
+
<rect x="180" y="154" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
31
|
+
<rect x="210" y="154" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
32
|
+
<rect x="240" y="154" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
33
|
+
<rect x="270" y="154" width="26" height="24" rx="4" fill="#eef0f3"/>
|
|
34
|
+
</svg>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="InvoicingOverview">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="70" height="34" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
6
|
+
<rect x="160" y="40" width="70" height="34" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
7
|
+
<rect x="236" y="40" width="70" height="34" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
8
|
+
<rect x="84" y="82" width="150" height="106" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
9
|
+
<rect x="242" y="82" width="66" height="106" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
10
|
+
<rect x="100" y="191" width="16" height="14" rx="4" fill="#16a34a"/>
|
|
11
|
+
<rect x="124" y="161" width="16" height="34" rx="4" fill="#16a34a"/>
|
|
12
|
+
<rect x="148" y="176" width="16" height="24" rx="4" fill="#16a34a"/>
|
|
13
|
+
<rect x="172" y="131" width="16" height="54" rx="4" fill="#16a34a"/>
|
|
14
|
+
<rect x="196" y="146" width="16" height="44" rx="4" fill="#16a34a"/>
|
|
15
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="InvoicingQuickCreateInvoice">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="224" height="148" rx="4" fill="#e4e7ec"/>
|
|
6
|
+
<rect x="120" y="56" width="152" height="120" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
7
|
+
<rect x="130" y="66" width="70" height="7" rx="3" fill="#16a34a"/>
|
|
8
|
+
<rect x="130" y="86" width="132" height="14" rx="4" fill="#eef0f3"/>
|
|
9
|
+
<rect x="130" y="108" width="132" height="14" rx="4" fill="#eef0f3"/>
|
|
10
|
+
<rect x="130" y="130" width="132" height="14" rx="4" fill="#eef0f3"/>
|
|
11
|
+
<rect x="206" y="152" width="56" height="16" rx="4" fill="#16a34a"/>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="InvoicingRecordPayment">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="224" height="148" rx="4" fill="#e4e7ec"/>
|
|
6
|
+
<rect x="120" y="56" width="152" height="120" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
7
|
+
<rect x="130" y="66" width="70" height="7" rx="3" fill="#16a34a"/>
|
|
8
|
+
<rect x="130" y="86" width="132" height="14" rx="4" fill="#eef0f3"/>
|
|
9
|
+
<rect x="130" y="108" width="132" height="14" rx="4" fill="#eef0f3"/>
|
|
10
|
+
<rect x="130" y="130" width="132" height="14" rx="4" fill="#eef0f3"/>
|
|
11
|
+
<rect x="206" y="152" width="56" height="16" rx="4" fill="#16a34a"/>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="PaymentsList">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
6
|
+
<rect x="88" y="44" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
7
|
+
<rect x="90" y="82" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
8
|
+
<rect x="90" y="94" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
9
|
+
<rect x="160" y="40" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
10
|
+
<rect x="164" y="44" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
11
|
+
<rect x="166" y="82" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
12
|
+
<rect x="166" y="94" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
13
|
+
<rect x="236" y="40" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
14
|
+
<rect x="240" y="44" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
15
|
+
<rect x="242" y="82" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
16
|
+
<rect x="242" y="94" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
17
|
+
<rect x="84" y="116" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
18
|
+
<rect x="88" y="120" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
19
|
+
<rect x="90" y="158" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
20
|
+
<rect x="90" y="170" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
21
|
+
<rect x="160" y="116" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
22
|
+
<rect x="164" y="120" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
23
|
+
<rect x="166" y="158" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
24
|
+
<rect x="166" y="170" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
25
|
+
<rect x="236" y="116" width="68" height="70" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
26
|
+
<rect x="240" y="120" width="60" height="30" rx="4" fill="#d5d9df"/>
|
|
27
|
+
<rect x="242" y="158" width="40" height="5" rx="3" fill="#16a34a"/>
|
|
28
|
+
<rect x="242" y="170" width="52" height="5" rx="3" fill="#9aa3b2"/>
|
|
29
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="200" viewBox="0 0 320 200" role="img" aria-label="Invoicing blueprint">
|
|
2
|
+
<rect x="0" y="0" width="320" height="200" rx="4" fill="#f4f5f7"/>
|
|
3
|
+
<rect x="12" y="12" width="296" height="20" rx="4" fill="#16a34a"/>
|
|
4
|
+
<rect x="12" y="40" width="64" height="148" rx="4" fill="#d5d9df"/>
|
|
5
|
+
<rect x="84" y="40" width="70" height="30" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
6
|
+
<rect x="160" y="40" width="70" height="30" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
7
|
+
<rect x="236" y="40" width="70" height="30" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
8
|
+
<rect x="84" y="78" width="224" height="110" rx="4" fill="#ffffff" stroke="#d5d9df"/>
|
|
9
|
+
<rect x="94" y="90" width="200" height="6" rx="3" fill="#9aa3b2"/>
|
|
10
|
+
<rect x="94" y="107" width="164" height="6" rx="3" fill="#9aa3b2"/>
|
|
11
|
+
<rect x="94" y="124" width="200" height="6" rx="3" fill="#9aa3b2"/>
|
|
12
|
+
<rect x="94" y="141" width="164" height="6" rx="3" fill="#9aa3b2"/>
|
|
13
|
+
<rect x="94" y="158" width="200" height="6" rx="3" fill="#9aa3b2"/>
|
|
14
|
+
</svg>
|