Skip to content

Commit 1631c35

Browse files
authored
Merge pull request #699 from openmsupply/688-manual-sync-extra
Manual sync extra
2 parents d0e5f92 + 7cf758f commit 1631c35

File tree

23 files changed

+358
-258
lines changed

23 files changed

+358
-258
lines changed

client/README.md

+76
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,82 @@ We're using [React Query](https://react-query.tanstack.com/overview) to query th
7777

7878
Check out the existing implementation using `api.ts` files and integration with the `DataTable` component.
7979

80+
React Query is a higher level package which manages caching and provides useful hooks for implementation. The API communication is using the package `graphql-request`. For this we've implemented a wrapper around `request` within the component `GqlContext` in order to catch gql errors and raise them up to React Query. The api is returning valid (200) responses with an error object when there is an error. React Query is expecting an error to be raised - so the implementation in `GqlContext` is interrogating responses and checking for errors.
81+
82+
When using `useQuery`, caching is handled for you. Provide a cache key as in the example usages here. When using a mutation we typically don't want to cache the response, so no cacheKey is used.
83+
84+
Cache keys are simply an object which is used as a stable reference for the cache. We are using a hierarchy for the cache keys and pass in an array of strings as the actual key. For example, in the outbound shipments, we have a base key of the string `outbound`. For a specific invoice, the cache key consists of the base, the store ID and the invoice ID. If you invalidate the base cache key of 'outbound' then the cache will be cleared for all invoices. To be more targeted, you could invalidate for a specific invoice, or for all invoices in a store.
85+
86+
An example of the pattern of hooks we've implemented when working with api calls is shown below. This is a simplified version of the outbound shipment implementation:
87+
88+
```
89+
api/
90+
├─ hooks/
91+
│ ├─ document/
92+
│ │ ├─ ...
93+
│ │ ├─ index.ts
94+
│ │ ├─ useOutbound.ts
95+
│ ├─ line/
96+
│ │ ├─ index.ts
97+
│ │ ├─ useOutboundLines.ts
98+
│ └─ utils/
99+
│ ├─ index.ts
100+
│ └─ useOutboundApi.ts
101+
├─ api.ts
102+
├─ index.ts
103+
├─ operations.generated.ts
104+
└─ operations.graphql
105+
106+
```
107+
108+
The cache keys are created in `use_____Api.ts`. For example, in `useOutboundApi.ts` there is the following:
109+
110+
```
111+
const keys = {
112+
base: () => ['outbound'] as const,
113+
detail: (id: string) => [...keys.base(), storeId, id] as const,
114+
list: () => [...keys.base(), storeId, 'list'] as const,
115+
paramList: (params: ListParams) => [...keys.list(), params] as const,
116+
sortedList: (sortBy: SortBy<OutboundRowFragment>) =>
117+
[...keys.list(), sortBy] as const,
118+
};
119+
```
120+
121+
and you will see the keys being referenced in other hooks, like so:
122+
123+
```
124+
const api = useOutboundApi();
125+
useQuery(api.keys.paramList(queryParams), ...
126+
```
127+
128+
129+
where the file `hooks/index.ts` has something like this in it:
130+
131+
```
132+
import { Utils } from './utils';
133+
import { Lines } from './line';
134+
import { Document } from './document';
135+
136+
export const useOutbound = {
137+
utils: {
138+
api: Utils.useOutboundApi,
139+
...
140+
},
141+
142+
document: {
143+
get: Document.useOutbound,
144+
...
145+
},
146+
147+
line: {
148+
stockLines: Lines.useOutboundLines,
149+
...
150+
},
151+
};
152+
```
153+
154+
The queries are implemented in `api.ts`. If you need to modify the shape of the data returned, then the preferred approach is to implement that in the api query. If required, this can also be done in the hook which calls the query, though this isn't idiomatic.
155+
80156
## Localisation
81157

82158
We're using [react-i18next](https://react.i18next.com/) for localisations. Collections of translatable items are grouped into namespaces so that we can reduce bundle sizes and keep files contained to specific areas. The namespace files are json files - kept separate from the main bundles and downloaded on demand. These are also cached locally in the browser.

client/codegen.yml

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
overwrite: true
2-
schema:
3-
[
4-
'https://demo-open.msupply.org/graphql',
5-
'./packages/common/src/additions.schema.graphql',
6-
]
2+
schema: [ '../server/schema.graphql' ]
73
generates:
84
./packages/common/src/types/schema.ts:
95
plugins:

client/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"test": "jest --config ./jest.config.js --maxWorkers=50% --env=jsdom",
1717
"storybook": "start-storybook -p 6006",
1818
"build-storybook": "build-storybook",
19-
"generate": "graphql-codegen --config codegen.yml",
19+
"generate": "cd ../server && cargo run --bin remote_server_cli -- export-graphql-schema && cd ../client && graphql-codegen --config codegen.yml",
2020
"android:run": "npx cap run android",
2121
"android:build:server": "cd ./packages/android && ./build_remote_server_libs.sh",
2222
"android:build:debug": "yarn build && npx cap copy && cd ./packages/android && ./gradlew assembleDebug",

client/packages/common/src/additions.schema.graphql

Whitespace-only changes.

client/packages/common/src/intl/locales/en/common.json

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@
190190
"log.requisition-deleted": "Requisition deleted",
191191
"log.requisition-status-sent": "Requisition sent",
192192
"log.requisition-status-finalised": "Requisition finalised",
193+
"messages.ago": "{{time}} ago",
193194
"messages.cant-delete-generic": "You cannot delete one or more of the selected items",
194195
"messages.confirm-cancel-generic": "You will lose any changes you have made to this form",
195196
"messages.confirm-delete-generic": "This will permanently remove data",

client/packages/common/src/intl/locales/fr/common.json

+1
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
"label.verified": "Vérifiée",
157157
"label.website": "Site Internet",
158158
"link.copy-to-clipboard": "Copier vers le Presse-papier",
159+
"messages.ago": "il y a {{time}}",
159160
"messages.delete-this-line": "Supprimer cette ligne",
160161
"placeholder.filter-items": "Filtrer les articles",
161162
"placeholder.search-by-name": "Rechercher par nom",

client/packages/common/src/intl/utils/DateUtils.ts

+18-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
format,
1313
parseISO,
1414
fromUnixTime,
15+
formatRelative,
16+
formatDistanceToNow,
1517
} from 'date-fns';
1618
import { enGB, enUS, fr, ar } from 'date-fns/locale';
1719

@@ -69,7 +71,21 @@ export const useFormatDateTime = () => {
6971
date: Date | string | number,
7072
formatString: string
7173
): string => format(dateInputHandler(date), formatString, { locale });
72-
// Add more date/time formatters as required
7374

74-
return { localisedDate, localisedTime, dayMonthShort, customDate };
75+
const relativeDateTime = (
76+
date: Date | string | number,
77+
baseDate: Date = new Date()
78+
): string => formatRelative(dateInputHandler(date), baseDate, { locale });
79+
80+
const localisedDistanceToNow = (date: Date | string | number) =>
81+
formatDistanceToNow(dateInputHandler(date), { locale });
82+
83+
return {
84+
customDate,
85+
dayMonthShort,
86+
localisedDate,
87+
localisedDistanceToNow,
88+
localisedTime,
89+
relativeDateTime,
90+
};
7591
};

client/packages/common/src/types/schema.ts

+35-62
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,31 @@ export type Scalars = {
1010
Boolean: boolean;
1111
Int: number;
1212
Float: number;
13+
/**
14+
* Implement the DateTime<Utc> scalar
15+
*
16+
* The input/output is a string in RFC3339 format.
17+
*/
1318
DateTime: string;
19+
/** A scalar that can represent any JSON value. */
1420
JSON: any;
21+
/**
22+
* ISO 8601 calendar date without timezone.
23+
* Format: %Y-%m-%d
24+
*
25+
* # Examples
26+
*
27+
* * `1994-11-13`
28+
* * `2000-02-24`
29+
*/
1530
NaiveDate: string;
31+
/**
32+
* ISO 8601 combined date and time without timezone.
33+
*
34+
* # Examples
35+
*
36+
* * `2015-07-01T08:59:60.123`,
37+
*/
1638
NaiveDateTime: string;
1739
};
1840

@@ -70,7 +92,7 @@ export enum ActivityLogSortFieldInput {
7092

7193
export type ActivityLogSortInput = {
7294
/**
73-
* Sort query result is sorted descending or ascending (if not provided the default is
95+
* Sort query result is sorted descending or ascending (if not provided the default is
7496
* ascending)
7597
*/
7698
desc?: InputMaybe<Scalars['Boolean']>;
@@ -732,11 +754,8 @@ export type InboundInvoiceCounts = {
732754
};
733755

734756
export enum InitialisationStatusType {
735-
/** Fuly initialised */
736757
Initialised = 'INITIALISED',
737-
/** Sync settings were set and sync was attempted at least once */
738758
Initialising = 'INITIALISING',
739-
/** Sync settings are not set and sync was not attempted */
740759
PreInitialisation = 'PRE_INITIALISATION'
741760
}
742761

@@ -1201,48 +1220,11 @@ export type InvoiceNodeOtherPartyArgs = {
12011220
};
12021221

12031222
export enum InvoiceNodeStatus {
1204-
/**
1205-
* General description: Outbound Shipment is ready for picking (all unallocated lines need to be fullfilled)
1206-
* Outbound Shipment: Invoice can only be turned to allocated status when
1207-
* all unallocated lines are fullfilled
1208-
* Inbound Shipment: not applicable
1209-
*/
12101223
Allocated = 'ALLOCATED',
1211-
/**
1212-
* General description: Inbound Shipment was received
1213-
* Outbound Shipment: Status is updated based on corresponding inbound Shipment
1214-
* Inbound Shipment: Stock is introduced and can be issued
1215-
*/
12161224
Delivered = 'DELIVERED',
1217-
/**
1218-
* Outbound Shipment: available_number_of_packs in a stock line gets
1219-
* updated when items are added to the invoice.
1220-
* Inbound Shipment: No stock changes in this status, only manually entered
1221-
* inbound Shipments have new status
1222-
*/
12231225
New = 'NEW',
1224-
/**
1225-
* General description: Outbound Shipment was picked from shelf and ready for Shipment
1226-
* Outbound Shipment: available_number_of_packs and
1227-
* total_number_of_packs get updated when items are added to the invoice
1228-
* Inbound Shipment: For inter store stock transfers an inbound Shipment
1229-
* is created when corresponding outbound Shipment is picked and ready for
1230-
* Shipment, inbound Shipment is not editable in this status
1231-
*/
12321226
Picked = 'PICKED',
1233-
/**
1234-
* General description: Outbound Shipment is sent out for delivery
1235-
* Outbound Shipment: Becomes not editable
1236-
* Inbound Shipment: For inter store stock transfers an inbound Shipment
1237-
* becomes editable when this status is set as a result of corresponding
1238-
* outbound Shipment being chagned to shipped (this is similar to New status)
1239-
*/
12401227
Shipped = 'SHIPPED',
1241-
/**
1242-
* General description: Received inbound Shipment was counted and verified
1243-
* Outbound Shipment: Status is updated based on corresponding inbound Shipment
1244-
* Inbound Shipment: Becomes not editable
1245-
*/
12461228
Verified = 'VERIFIED'
12471229
}
12481230

@@ -1272,7 +1254,7 @@ export enum InvoiceSortFieldInput {
12721254

12731255
export type InvoiceSortInput = {
12741256
/**
1275-
* Sort query result is sorted descending or ascending (if not provided the default is
1257+
* Sort query result is sorted descending or ascending (if not provided the default is
12761258
* ascending)
12771259
*/
12781260
desc?: InputMaybe<Scalars['Boolean']>;
@@ -1354,7 +1336,7 @@ export enum ItemSortFieldInput {
13541336

13551337
export type ItemSortInput = {
13561338
/**
1357-
* Sort query result is sorted descending or ascending (if not provided the default is
1339+
* Sort query result is sorted descending or ascending (if not provided the default is
13581340
* ascending)
13591341
*/
13601342
desc?: InputMaybe<Scalars['Boolean']>;
@@ -1417,7 +1399,7 @@ export enum LocationSortFieldInput {
14171399

14181400
export type LocationSortInput = {
14191401
/**
1420-
* Sort query result is sorted descending or ascending (if not provided the default is
1402+
* Sort query result is sorted descending or ascending (if not provided the default is
14211403
* ascending)
14221404
*/
14231405
desc?: InputMaybe<Scalars['Boolean']>;
@@ -1491,7 +1473,7 @@ export enum MasterListSortFieldInput {
14911473

14921474
export type MasterListSortInput = {
14931475
/**
1494-
* Sort query result is sorted descending or ascending (if not provided the default is
1476+
* Sort query result is sorted descending or ascending (if not provided the default is
14951477
* ascending)
14961478
*/
14971479
desc?: InputMaybe<Scalars['Boolean']>;
@@ -1888,7 +1870,7 @@ export type NameFilterInput = {
18881870
/** Filter by supplier property */
18891871
isSupplier?: InputMaybe<Scalars['Boolean']>;
18901872
/**
1891-
* Show system names (defaults to false)
1873+
* Show system names (defaults to false)
18921874
* System names don't have name_store_join thus if queried with true filter, is_visible filter should also be true or null
18931875
* if is_visible is set to true and is_system_name is also true no system names will be returned
18941876
*/
@@ -1946,7 +1928,7 @@ export enum NameSortFieldInput {
19461928

19471929
export type NameSortInput = {
19481930
/**
1949-
* Sort query result is sorted descending or ascending (if not provided the default is
1931+
* Sort query result is sorted descending or ascending (if not provided the default is
19501932
* ascending)
19511933
*/
19521934
desc?: InputMaybe<Scalars['Boolean']>;
@@ -2355,7 +2337,7 @@ export enum ReportSortFieldInput {
23552337

23562338
export type ReportSortInput = {
23572339
/**
2358-
* Sort query result is sorted descending or ascending (if not provided the default is
2340+
* Sort query result is sorted descending or ascending (if not provided the default is
23592341
* ascending)
23602342
*/
23612343
desc?: InputMaybe<Scalars['Boolean']>;
@@ -2500,23 +2482,14 @@ export type RequisitionNodeOtherPartyArgs = {
25002482
};
25012483

25022484
export enum RequisitionNodeStatus {
2503-
/** New requisition when manually created */
25042485
Draft = 'DRAFT',
2505-
/**
2506-
* Response requisition: When supplier finished fulfilling requisition, locked for future editing
2507-
* Request requisition: When response requisition is finalised
2508-
*/
25092486
Finalised = 'FINALISED',
2510-
/** New requisition when automatically created, only applicable to response requisition when it's duplicated in supplying store from request requisition */
25112487
New = 'NEW',
2512-
/** Request requisition is sent and locked for future editing, only applicable to request requisition */
25132488
Sent = 'SENT'
25142489
}
25152490

25162491
export enum RequisitionNodeType {
2517-
/** Requisition created by store that is ordering stock */
25182492
Request = 'REQUEST',
2519-
/** Supplying store requisition in response to request requisition */
25202493
Response = 'RESPONSE'
25212494
}
25222495

@@ -2537,7 +2510,7 @@ export enum RequisitionSortFieldInput {
25372510

25382511
export type RequisitionSortInput = {
25392512
/**
2540-
* Sort query result is sorted descending or ascending (if not provided the default is
2513+
* Sort query result is sorted descending or ascending (if not provided the default is
25412514
* ascending)
25422515
*/
25432516
desc?: InputMaybe<Scalars['Boolean']>;
@@ -2716,7 +2689,7 @@ export enum StocktakeSortFieldInput {
27162689

27172690
export type StocktakeSortInput = {
27182691
/**
2719-
* Sort query result is sorted descending or ascending (if not provided the default is
2692+
* Sort query result is sorted descending or ascending (if not provided the default is
27202693
* ascending)
27212694
*/
27222695
desc?: InputMaybe<Scalars['Boolean']>;
@@ -2764,7 +2737,7 @@ export enum StoreSortFieldInput {
27642737

27652738
export type StoreSortInput = {
27662739
/**
2767-
* Sort query result is sorted descending or ascending (if not provided the default is
2740+
* Sort query result is sorted descending or ascending (if not provided the default is
27682741
* ascending)
27692742
*/
27702743
desc?: InputMaybe<Scalars['Boolean']>;
@@ -3003,12 +2976,12 @@ export type UpdateOutboundShipmentInput = {
30032976
id: Scalars['String'];
30042977
onHold?: InputMaybe<Scalars['Boolean']>;
30052978
/**
3006-
* The other party must be a customer of the current store.
2979+
* The other party must be a customer of the current store.
30072980
* This field can be used to change the other_party of an invoice
30082981
*/
30092982
otherPartyId?: InputMaybe<Scalars['String']>;
30102983
/**
3011-
* When changing the status from DRAFT to CONFIRMED or FINALISED the total_number_of_packs for
2984+
* When changing the status from DRAFT to CONFIRMED or FINALISED the total_number_of_packs for
30122985
* existing invoice items gets updated.
30132986
*/
30142987
status?: InputMaybe<UpdateOutboundShipmentStatusInput>;

0 commit comments

Comments
 (0)