Skip to content

Commit d29e377

Browse files
authored
add contribution modal, cell morphology creation (#914)
1 parent 94a93a6 commit d29e377

File tree

76 files changed

+5837
-129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+5837
-129
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@
8181
"d3": "^7.8.4",
8282
"d3-sankey": "^0.12.3",
8383
"date-fns": "^4.1.0",
84+
"dayjs": "^1.11.18",
8485
"deepdash-es": "^5.3.9",
8586
"distinct-colors": "^3.0.0",
87+
"embla-carousel-fade": "^8.6.0",
8688
"embla-carousel-react": "^8.6.0",
8789
"embla-carousel-wheel-gestures": "^8.1.0",
8890
"fast-csv": "^5.0.2",

pnpm-lock.yaml

Lines changed: 23 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api/apiClient.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ class ApiClient {
448448
* @param {CacheConfiguration} [cacheConfig] - optional cache configuration
449449
* @returns {Promise<ApiClient>} a promise that resolves to an instance of ApiClient
450450
*/
451-
export default async function authApiClient(rootUri: string, cacheConfig?: CacheConfiguration) {
451+
export async function authApiClient(rootUri: string, cacheConfig?: CacheConfiguration) {
452452
const session = await getSession();
453453

454454
return new ApiClient({
@@ -457,3 +457,5 @@ export default async function authApiClient(rootUri: string, cacheConfig?: Cache
457457
cache: cacheConfig,
458458
});
459459
}
460+
461+
export default authApiClient;

src/api/entitycore/queries/annotations/etype.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import authApiClient from '@/api/apiClient';
21
import { getEntityCoreContext } from '@/api/entitycore/utils';
2+
import { authApiClient } from '@/api/apiClient';
33
import { entityCoreUrl } from '@/config';
44

55
import type { EntityCoreResponse } from '@/api/entitycore/types/shared/response';
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import z from 'zod';
2+
3+
import { getEntityCoreContext } from '@/api/entitycore/utils';
4+
import { authApiClient } from '@/api/apiClient';
5+
import { entityCoreUrl } from '@/config';
6+
7+
import type { IMTypeClassification } from '@/api/entitycore/types/shared/global';
8+
import type { EntityCoreResponse } from '@/api/entitycore/types/shared/response';
9+
import type { WorkspaceContext } from '@/types/common';
10+
11+
const baseUri = '/mtype-classification';
12+
/**
13+
* Retrieves a list of mtype classifications from the EntityCoreAPI.
14+
15+
* @returns {Promise<EntityCoreResponse<IMType>>} A promise that resolves to the list of mtypes
16+
*/
17+
export async function getMtypeClassifications({
18+
filters,
19+
ctx,
20+
}: {
21+
filters?: any;
22+
ctx?: WorkspaceContext;
23+
}) {
24+
const api = await authApiClient(entityCoreUrl);
25+
return await api.get<EntityCoreResponse<IMTypeClassification>>(baseUri, {
26+
...getEntityCoreContext(ctx),
27+
queryParams: {
28+
...filters,
29+
},
30+
});
31+
}
32+
33+
/**
34+
* Retrieves one mtype classification from the EntityCoreAPI.
35+
36+
* @returns {Promise<IMType>} A promise that resolves to the single mtype
37+
*/
38+
export async function getMtypeClassification({ id }: { id: string }) {
39+
const api = await authApiClient(entityCoreUrl);
40+
return await api.get<IMTypeClassification>(`${baseUri}/${id}`, undefined, {
41+
cache: { cacheName: 'mtype', enabled: true, ttlInSeconds: 86_400 },
42+
});
43+
}
44+
45+
export const mtypeClassificationCreateSchema = z.object({
46+
authorized_public: z.boolean(),
47+
entity_id: z.string().uuid(),
48+
mtype_class_id: z.string().uuid(),
49+
});
50+
51+
export type TMtypeClassificationCreate = z.infer<typeof mtypeClassificationCreateSchema>;
52+
53+
/**
54+
* Creates a new mtype classification
55+
* @param param0
56+
* @returns A promise that resolves to the created mtype classification
57+
*/
58+
export async function createMtypeClassification({
59+
context,
60+
payload,
61+
}: {
62+
context: WorkspaceContext;
63+
payload: TMtypeClassificationCreate;
64+
}) {
65+
const api = await authApiClient(entityCoreUrl);
66+
return await api.post<IMTypeClassification>(baseUri, {
67+
...getEntityCoreContext(context),
68+
headers: {
69+
accept: 'application/json',
70+
'content-type': 'application/json',
71+
...getEntityCoreContext(context).headers,
72+
},
73+
body: payload,
74+
});
75+
}

src/api/entitycore/queries/annotations/mtype.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import authApiClient from '@/api/apiClient';
21
import { getEntityCoreContext } from '@/api/entitycore/utils';
2+
import { authApiClient } from '@/api/apiClient';
33
import { entityCoreUrl } from '@/config';
44

55
import type { IMType, IMTypeFilter } from '@/api/entitycore/types/shared/global';

src/api/entitycore/queries/assets/index.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import kebabCase from 'lodash/kebabCase';
22

3-
import authApiClient from '@/api/apiClient';
4-
53
import { TEntityTypeDict } from '@/api/entitycore/types/entity-type';
64
import { getEntityCoreContext } from '@/api/entitycore/utils';
75
import { compactRecord } from '@/utils/dictionary';
6+
import { authApiClient } from '@/api/apiClient';
87
import { entityCoreUrl } from '@/config';
98

109
import type {
@@ -216,3 +215,42 @@ export async function listDirectoryOfAssets({
216215
{ retryOnError }
217216
);
218217
}
218+
219+
export async function createAsset({
220+
ctx,
221+
entityType,
222+
entityId,
223+
fileName,
224+
payload,
225+
meta,
226+
label,
227+
mimeType,
228+
}: {
229+
ctx?: WorkspaceContext;
230+
entityType: EntityCoreDataType;
231+
entityId: string;
232+
fileName: string;
233+
mimeType: string;
234+
payload: BlobPart;
235+
meta?: Record<string, any>;
236+
label?: AssetLabel;
237+
}): Promise<IAsset> {
238+
const blob = new Blob([payload], { type: mimeType });
239+
const file = new File([blob], fileName, { type: mimeType });
240+
const formData = new FormData();
241+
242+
if (file) formData.append('file', file);
243+
if (label) formData.append('label', label);
244+
if (meta) formData.append('meta', JSON.stringify(meta));
245+
246+
const api = await authApiClient(entityCoreUrl);
247+
return await api.post<IAsset>(`/${kebabCase(entityType)}/${entityId}/assets`, {
248+
headers: {
249+
...getEntityCoreContext(ctx).headers,
250+
// This is required due apiClient is using "application/json" as default content-type
251+
// the browser should handle auto the multipart-form
252+
'Content-Type': undefined,
253+
},
254+
body: formData,
255+
});
256+
}

src/api/entitycore/queries/experimental/cell-morphology.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import z from 'zod';
12
import { entityCoreApi, getEntityCoreContext } from '@/api/entitycore/utils';
23

34
import type { EntityCoreResponse } from '@/api/entitycore/types/shared/response';
@@ -69,3 +70,56 @@ export async function getCellMorphology({
6970
},
7071
});
7172
}
73+
74+
const cellMorphologySchema = z.object({
75+
name: z
76+
.string({ message: 'Cell morphology name is required' })
77+
.nonempty({ message: 'Cell morphology name is required' }),
78+
description: z
79+
.string({ message: 'Cell morphology description is required' })
80+
.nonempty({ message: 'Cell morphology description is required' }),
81+
brain_region_id: z
82+
.string({ message: 'Brain region is required' })
83+
.uuid()
84+
.nonempty({ message: 'Brain region is required' }),
85+
subject_id: z
86+
.string({ message: 'Subject is required' })
87+
.uuid()
88+
.nonempty({ message: 'Subject is required' }),
89+
license_id: z
90+
.string({ message: 'License is required' })
91+
.uuid()
92+
.nonempty({ message: 'License is required' }),
93+
experiment_date: z.string({ message: 'Experiment date is required' }).nullish(),
94+
contact_email: z
95+
.string({ message: 'Contact email is required' })
96+
.email({ message: 'Contact email is required' })
97+
.nullish(),
98+
published_in: z.string({ message: 'Published in is required' }).nullish(),
99+
location: z.object({ x: z.number(), y: z.number(), z: z.number() }).nullable(),
100+
});
101+
102+
export type TCellMorphologyCreate = z.infer<typeof cellMorphologySchema>;
103+
104+
/**
105+
* Creates a new cell morphology
106+
* @param param0
107+
* @returns A promise that resolves to the created cell morphology
108+
*/
109+
export async function createCellMorphology({
110+
context,
111+
payload,
112+
}: {
113+
context?: WorkspaceContext | null;
114+
payload: TCellMorphologyCreate;
115+
}) {
116+
const api = await entityCoreApi();
117+
return await api.post<ICellMorphology>(baseUri, {
118+
headers: {
119+
accept: 'application/json',
120+
'content-type': 'application/json',
121+
...getEntityCoreContext(context).headers,
122+
},
123+
body: payload,
124+
});
125+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { authApiClient } from '@/api/apiClient';
2+
import { entityCoreUrl } from '@/config';
3+
4+
import type { EntityCoreResponse } from '@/api/entitycore/types/shared/response';
5+
import type { IConsortiumFilter } from '@/api/entitycore/types/entities/agent';
6+
import type { IConsortium } from '@/api/entitycore/types/shared/global';
7+
8+
const baseUri = '/consortium';
9+
/**
10+
* Retrieves a list of consortia from the EntityCoreAPI.
11+
12+
* @returns {Promise<EntityCoreResponse<IConsortium>>} A promise that resolves to the list of consortia
13+
*/
14+
export async function getConsortia({ filters }: { filters: Partial<IConsortiumFilter> }) {
15+
const api = await authApiClient(entityCoreUrl);
16+
return await api.get<EntityCoreResponse<IConsortium>>(baseUri, {
17+
queryParams: {
18+
...filters,
19+
},
20+
});
21+
}
22+
23+
/**
24+
* Retrieves one consortium from the EntityCoreAPI.
25+
26+
* @returns {Promise<IConsortium>} A promise that resolves to the single consortium
27+
*/
28+
export async function getConsortium({ id }: { id: string }) {
29+
const api = await authApiClient(entityCoreUrl);
30+
return await api.get<IConsortium>(`${baseUri}/${id}`);
31+
}

0 commit comments

Comments
 (0)