· 4 years ago · Aug 17, 2021, 08:44 AM
1
2Skip to content
3Pull requests
4Issues
5Marketplace
6Explore
7@NiFos
8ColabraHQ /
9colabra
10Private
11
123
130
14
15 0
16
17Code
18Issues
19Pull requests 12
20Actions
21Projects
22Wiki
23Security
24
25 Insights
26
27feat: implement tags #138
28Draft
29NiFos wants to merge 10 commits into main from vladnifos/pro-243-tags
30+1,466 −37
31Conversation 2
32Commits 10
33Checks 4
34Files changed 78
35Draft
36feat: implement tags
37#138
38File filter
390 / 78 files viewed
4064 api/hasura/metadata/actions.graphql
41Viewed
42@@ -4,100 +4,110 @@ type Mutation {
43 ): accept_invitation_output!
44}
45
46
47type Mutation {
48 authenticate (
49 didToken: String!
50 ): AuthOutput!
51}
52
53
54type Mutation {
55 create_experiment (
56 payload: create_experiment_input!
57 ): create_experiment_output!
58}
59
60
61type Mutation {
62 create_project (
63 payload: create_project_input
64 ): create_project_output!
65}
66
67type Mutation {
68 create_tag (
69 payload: create_tag_input!
70 ): create_tag_output!
71}
72
73type Mutation {
74 create_template (
75 payload: create_template_input!
76 ): create_template_output!
77}
78
79type Mutation {
80 create_tag (
81 payload: create_tag_input!
82 ): create_tag_output!
83}
84
85
86type Mutation {
87 create_template (
88 payload: create_template_input!
89 ): create_template_output!
90}
91
92
93type Mutation {
94 create_workspace (
95 payload: create_workspace_input
96 ): create_workspace_output!
97}
98
99
100type Mutation {
101 delete_collaborator (
102 payload: delete_collaborator_input!
103 ): delete_collaborator_output!
104}
105
106
107type Mutation {
108 duplicate_experiment (
109 payload: duplicate_experiment_input!
110 ): duplicate_experiment_output!
111}
112
113
114type Mutation {
115 export_project (
116 id: uuid!
117 ): export_data_output!
118}
119
120
121type Mutation {
122 finish_onboarding (
123 payload: finish_onboarding_input!
124 ): finish_onboarding_output!
125}
126
127
128type Mutation {
129 generate_signed_url (
130 payload: generate_signed_url_input!
131 ): generate_signed_url_output!
132}
133
134
135type Mutation {
136 invite_collaborator (
137 payload: invite_collaborator_input!
138 ): invite_collaborator_output!
139}
140
141
142type Mutation {
143 summarize_scans (
144 payload: summarize_scans_input!
145 ): summarize_scans_output!
146}
147
148
149type Mutation {
150 update_collaborator (
151 payload: update_collaborator_input!
152 ): update_collaborator_output!
153}
154
155
156type Mutation {
157 upload_scan (
158 payload: upload_scan_input!
159 ): upload_scan_output!
160}
161
162
163
164
165input invite_collaborator_input {
166 invited_to_project : uuid
167 workspace_id : uuid!
168@@ -136,7 +146,6 @@ input invite_project_collaborator_input {
169}
170
171input create_experiment_input {
172 name : String
173 project_id : uuid!
174}
175
176@@ -197,6 +206,17 @@ input summarize_scans_input {
177 summary : String!
178}
179
180input create_template_input {
181 workspace_id : uuid!
182}
183
184input create_tag_input {
185 name : String!
186 default_unit : uuid!
187 color : String!
188 workspace_id : uuid!
189}
190
191type AuthOutput {
192 success : Boolean!
193 token : String!
194@@ -278,3 +298,15 @@ type summarize_scans_output {
195 success : Boolean!
196}
197
198type TemplateOutput {
199 id : uuid!
200}
201
202type create_template_output {
203 template : jsonb!
204}
205
206type create_tag_output {
207 tag : jsonb!
208}
209
21023 api/hasura/metadata/actions.yaml
211Viewed
212@@ -36,6 +36,24 @@ actions:
213 value_from_env: FUNCTIONS_API_KEY
214 permissions:
215 - role: user
216- name: create_tag
217 definition:
218 kind: synchronous
219 handler: '{{FUNCTIONS_ENDPOINT}}/actions-create-tag'
220 headers:
221 - name: x-api-key
222 value_from_env: FUNCTIONS_API_KEY
223 permissions:
224 - role: user
225- name: create_template
226 definition:
227 kind: synchronous
228 handler: '{{FUNCTIONS_ENDPOINT}}/actions-create-template'
229 headers:
230 - name: x-api-key
231 value_from_env: FUNCTIONS_API_KEY
232 permissions:
233 - role: user
234- name: create_workspace
235 definition:
236 kind: synchronous
237@@ -149,6 +167,8 @@ custom_types:
238 - name: finish_onboarding_input
239 - name: summarize_notes_input
240 - name: summarize_scans_input
241 - name: create_template_input
242 - name: create_tag_input
243 objects:
244 - name: AuthOutput
245 - name: InviteCollaboratorOutput
246@@ -196,4 +216,7 @@ custom_types:
247 - name: finish_onboarding_output
248 - name: summarize_notes_output
249 - name: summarize_scans_output
250 - name: TemplateOutput
251 - name: create_template_output
252 - name: create_tag_output
253 scalars: []
25461 api/hasura/metadata/databases/default/tables/public_experiments_tags.yaml
255Viewed
256@@ -0,0 +1,61 @@
257table:
258 name: experiments_tags
259 schema: public
260object_relationships:
261- name: experiment
262 using:
263 foreign_key_constraint_on: experiment_id
264- name: tag
265 using:
266 foreign_key_constraint_on: tag_id
267select_permissions:
268- permission:
269 allow_aggregations: true
270 columns:
271 - id
272 - tag_id
273 - experiment_id
274 - unit
275 - value
276 filter:
277 experiment:
278 project:
279 workspace:
280 collaborators:
281 collaborator_id:
282 _eq: X-Hasura-User-Id
283 role: user
284update_permissions:
285- permission:
286 check: null
287 columns:
288 - unit
289 - value
290 filter:
291 experiment:
292 project:
293 workspace:
294 collaborators:
295 _and:
296 - collaborator_id:
297 _eq: X-Hasura-User-Id
298 - role:
299 _in:
300 - OWNER
301 - EDITOR
302 role: user
303delete_permissions:
304- permission:
305 filter:
306 experiment:
307 project:
308 workspace:
309 collaborators:
310 _and:
311 - collaborator_id:
312 _eq: X-Hasura-User-Id
313 - role:
314 _in:
315 - OWNER
316 - EDITOR
317 role: user
3187 api/hasura/metadata/databases/default/tables/public_projects.yaml
319Viewed
320@@ -46,13 +46,6 @@ array_relationships:
321 table:
322 name: invitations
323 schema: public
324- name: project_tags
325 using:
326 foreign_key_constraint_on:
327 column: project_id
328 table:
329 name: project_tag
330 schema: public
331select_permissions:
332- permission:
333 columns:
33436 api/hasura/metadata/databases/default/tables/public_tag_unit.yaml
335Viewed
336@@ -0,0 +1,36 @@
337table:
338 name: tag_unit
339 schema: public
340object_relationships:
341- name: workspace
342 using:
343 foreign_key_constraint_on: workspace_id
344select_permissions:
345- permission:
346 columns:
347 - id
348 - workspace_id
349 - unit
350 filter:
351 workspace:
352 collaborators:
353 collaborator_id:
354 _eq: X-Hasura-User-Id
355 role: user
356update_permissions:
357- permission:
358 check: null
359 columns:
360 - is_deleted
361 - unit
362 filter:
363 workspace:
364 collaborators:
365 _and:
366 - collaborator_id:
367 _eq: X-Hasura-User-Id
368 - role:
369 _in:
370 - OWNER
371 - EDITOR
372 role: user
37358 api/hasura/metadata/databases/default/tables/public_tags.yaml
374Viewed
375@@ -1,19 +1,71 @@
376table:
377 name: tags
378 schema: public
379object_relationships:
380- name: author
381 using:
382 foreign_key_constraint_on: author_id
383- name: tag_unit
384 using:
385 foreign_key_constraint_on: default_unit
386- name: workspace
387 using:
388 foreign_key_constraint_on: workspace_id
389array_relationships:
390- name: project_tags
391- name: experiments_tags
392 using:
393 foreign_key_constraint_on:
394 column: tag_id
395 table:
396 name: project_tag
397 name: experiments_tags
398 schema: public
399select_permissions:
400- permission:
401 allow_aggregations: true
402 columns:
403 - author_id
404 - color
405 - created_at
406 - default_unit
407 - default_value
408 - id
409 - name
410 filter: {}
411 - updated_at
412 - workspace_id
413 filter:
414 _and:
415 - is_deleted:
416 _eq: false
417 - workspace:
418 collaborators:
419 _and:
420 - collaborator_id:
421 _eq: X-Hasura-User-Id
422 - role:
423 _in:
424 - OWNER
425 - EDITOR
426 role: user
427update_permissions:
428- permission:
429 check: null
430 columns:
431 - color
432 - default_unit
433 - default_value
434 - is_deleted
435 - name
436 filter:
437 _and:
438 - is_deleted:
439 _eq: false
440 - workspace:
441 collaborators:
442 _and:
443 - collaborator_id:
444 _eq: X-Hasura-User-Id
445 - role:
446 _in:
447 - OWNER
448 - EDITOR
449 role: user
45047 api/hasura/metadata/databases/default/tables/public_templates.yaml
451Viewed
452@@ -0,0 +1,47 @@
453table:
454 name: templates
455 schema: public
456object_relationships:
457- name: author
458 using:
459 foreign_key_constraint_on: author_id
460- name: workspace
461 using:
462 foreign_key_constraint_on: workspace_id
463select_permissions:
464- permission:
465 columns:
466 - author_id
467 - created_at
468 - id
469 - is_deleted
470 - name
471 - summary
472 - workspace_id
473 filter:
474 _and:
475 - workspace:
476 collaborators:
477 collaborator_id:
478 _eq: X-Hasura-User-Id
479 - is_deleted:
480 _eq: false
481 role: user
482update_permissions:
483- permission:
484 check: null
485 columns:
486 - is_deleted
487 - name
488 - summary
489 filter:
490 workspace:
491 collaborators:
492 _and:
493 - collaborator_id:
494 _eq: X-Hasura-User-Id
495 - role:
496 _in:
497 - OWNER
498 - EDITOR
499 role: user
50021 api/hasura/metadata/databases/default/tables/public_workspaces.yaml
501Viewed
502@@ -61,6 +61,27 @@ array_relationships:
503 table:
504 name: projects
505 schema: public
506- name: tag_units
507 using:
508 foreign_key_constraint_on:
509 column: workspace_id
510 table:
511 name: tag_unit
512 schema: public
513- name: tags
514 using:
515 foreign_key_constraint_on:
516 column: workspace_id
517 table:
518 name: tags
519 schema: public
520- name: templates
521 using:
522 foreign_key_constraint_on:
523 column: workspace_id
524 table:
525 name: templates
526 schema: public
527select_permissions:
528- permission:
529 columns:
5304 api/hasura/metadata/databases/default/tables/tables.yaml
531Viewed
532@@ -5,6 +5,7 @@
533- "!include public_experiment_activity.yaml"
534- "!include public_experiment_status.yaml"
535- "!include public_experiments.yaml"
536- "!include public_experiments_tags.yaml"
537- "!include public_files.yaml"
538- "!include public_invitations.yaml"
539- "!include public_lab_role.yaml"
540@@ -18,12 +19,13 @@
541- "!include public_private_scans.yaml"
542- "!include public_private_user_workspaces.yaml"
543- "!include public_project_collaborator.yaml"
544- "!include public_project_tag.yaml"
545- "!include public_projects.yaml"
546- "!include public_public_invitations.yaml"
547- "!include public_public_workspaces.yaml"
548- "!include public_scans.yaml"
549- "!include public_tag_unit.yaml"
550- "!include public_tags.yaml"
551- "!include public_templates.yaml"
552- "!include public_user_app_default_views.yaml"
553- "!include public_user_preferences.yaml"
554- "!include public_user_private.yaml"
5551 api/hasura/migrations/default/1623335538634_create_table_public_templates/down.sql
556Viewed
557@@ -0,0 +1 @@
558DROP TABLE "public"."templates";
5592 api/hasura/migrations/default/1623335538634_create_table_public_templates/up.sql
560Viewed
561@@ -0,0 +1,2 @@
562CREATE TABLE "public"."templates" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "name" text NOT NULL, "summary" text NOT NULL, "workspace_id" uuid NOT NULL, "author_id" uuid NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("author_id") REFERENCES "public"."users"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));
563CREATE EXTENSION IF NOT EXISTS pgcrypto;
5644 ...rations/default/1623335947371_alter_table_public_templates_add_column_created_at/down.sql
565Viewed
566@@ -0,0 +1,4 @@
567-- Could not auto-generate a down migration.
568-- Please write an appropriate down migration for the SQL below:
569-- alter table "public"."templates" add column "created_at" timestamptz
570 null default now();
5712 ...igrations/default/1623335947371_alter_table_public_templates_add_column_created_at/up.sql
572Viewed
573@@ -0,0 +1,2 @@
574alter table "public"."templates" add column "created_at" timestamptz
575 null default now();
5762 .../migrations/default/1623410456183_alter_table_public_templates_alter_column_name/down.sql
577Viewed
578@@ -0,0 +1,2 @@
579alter table "public"."templates" alter column "name" set not null;
580alter table "public"."templates" alter column "name" drop default;
5812 ...ra/migrations/default/1623410456183_alter_table_public_templates_alter_column_name/up.sql
582Viewed
583@@ -0,0 +1,2 @@
584alter table "public"."templates" alter column "name" set default 'Template';
585alter table "public"."templates" alter column "name" drop not null;
5861 ...grations/default/1623410501210_alter_table_public_templates_alter_column_summary/down.sql
587Viewed
588@@ -0,0 +1 @@
589alter table "public"."templates" alter column "summary" set not null;
5901 ...migrations/default/1623410501210_alter_table_public_templates_alter_column_summary/up.sql
591Viewed
592@@ -0,0 +1 @@
593alter table "public"."templates" alter column "summary" drop not null;
5944 ...rations/default/1623411389934_alter_table_public_templates_add_column_is_deleted/down.sql
595Viewed
596@@ -0,0 +1,4 @@
597-- Could not auto-generate a down migration.
598-- Please write an appropriate down migration for the SQL below:
599-- alter table "public"."templates" add column "is_deleted" boolean
600 null default 'false';
6012 ...igrations/default/1623411389934_alter_table_public_templates_add_column_is_deleted/up.sql
602Viewed
603@@ -0,0 +1,2 @@
604alter table "public"."templates" add column "is_deleted" boolean
605 null default 'false';
6064 ...migrations/default/1624277641709_alter_table_public_tags_add_column_workspace_id/down.sql
607Viewed
608@@ -0,0 +1,4 @@
609-- Could not auto-generate a down migration.
610-- Please write an appropriate down migration for the SQL below:
611-- alter table "public"."tags" add column "workspace_id" uuid
612 not null;
6132 ...a/migrations/default/1624277641709_alter_table_public_tags_add_column_workspace_id/up.sql
614Viewed
615@@ -0,0 +1,2 @@
616alter table "public"."tags" add column "workspace_id" uuid
617 not null;
6184 ...migrations/default/1624279039817_alter_table_public_tags_add_column_default_unit/down.sql
619Viewed
620@@ -0,0 +1,4 @@
621-- Could not auto-generate a down migration.
622-- Please write an appropriate down migration for the SQL below:
623-- alter table "public"."tags" add column "default_unit" uuid
624 null;
6252 ...a/migrations/default/1624279039817_alter_table_public_tags_add_column_default_unit/up.sql
626Viewed
627@@ -0,0 +1,2 @@
628alter table "public"."tags" add column "default_unit" uuid
629 null;
6304 ...igrations/default/1624279063984_alter_table_public_tags_add_column_default_value/down.sql
631Viewed
632@@ -0,0 +1,4 @@
633-- Could not auto-generate a down migration.
634-- Please write an appropriate down migration for the SQL below:
635-- alter table "public"."tags" add column "default_value" text
636 null;
6372 .../migrations/default/1624279063984_alter_table_public_tags_add_column_default_value/up.sql
638Viewed
639@@ -0,0 +1,2 @@
640alter table "public"."tags" add column "default_value" text
641 null;
6421 api/hasura/migrations/default/1624279247042_create_table_public_experiments_tags/down.sql
643Viewed
644@@ -0,0 +1 @@
645DROP TABLE "public"."experiments_tags";
6462 api/hasura/migrations/default/1624279247042_create_table_public_experiments_tags/up.sql
647Viewed
648@@ -0,0 +1,2 @@
649CREATE TABLE "public"."experiments_tags" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "tag_id" uuid NOT NULL, "experiment_id" uuid NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("experiment_id") REFERENCES "public"."experiments"("id") ON UPDATE restrict ON DELETE restrict, FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));
650CREATE EXTENSION IF NOT EXISTS pgcrypto;
6511 api/hasura/migrations/default/1624279292501_set_fk_public_tags_workspace_id/down.sql
652Viewed
653@@ -0,0 +1 @@
654alter table "public"."tags" drop constraint "tags_workspace_id_fkey";
6555 api/hasura/migrations/default/1624279292501_set_fk_public_tags_workspace_id/up.sql
656Viewed
657@@ -0,0 +1,5 @@
658alter table "public"."tags"
659 add constraint "tags_workspace_id_fkey"
660 foreign key ("workspace_id")
661 references "public"."workspaces"
662 ("id") on update restrict on delete restrict;
6633 api/hasura/migrations/default/1624279435581_drop_table_public_project_tag/down.sql
664Viewed
665@@ -0,0 +1,3 @@
666-- Could not auto-generate a down migration.
667-- Please write an appropriate down migration for the SQL below:
668-- DROP table "public"."project_tag";
6691 api/hasura/migrations/default/1624279435581_drop_table_public_project_tag/up.sql
670Viewed
671@@ -0,0 +1 @@
672DROP table "public"."project_tag";
6731 api/hasura/migrations/default/1624279624635_create_table_public_tag_unit/down.sql
674Viewed
675@@ -0,0 +1 @@
676DROP TABLE "public"."tag_unit";
6772 api/hasura/migrations/default/1624279624635_create_table_public_tag_unit/up.sql
678Viewed
679@@ -0,0 +1,2 @@
680CREATE TABLE "public"."tag_unit" ("id" uuid NOT NULL DEFAULT gen_random_uuid(), "tag_id" uuid NOT NULL, "unit" text NOT NULL, PRIMARY KEY ("id") , FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON UPDATE restrict ON DELETE restrict, UNIQUE ("id"));
681CREATE EXTENSION IF NOT EXISTS pgcrypto;
6824 ...ations/default/1624279667999_alter_table_public_experiments_tags_add_column_unit/down.sql
683Viewed
684@@ -0,0 +1,4 @@
685-- Could not auto-generate a down migration.
686-- Please write an appropriate down migration for the SQL below:
687-- alter table "public"."experiments_tags" add column "unit" uuid
688 null;
6892 ...grations/default/1624279667999_alter_table_public_experiments_tags_add_column_unit/up.sql
690Viewed
691@@ -0,0 +1,2 @@
692alter table "public"."experiments_tags" add column "unit" uuid
693 null;
6944 ...tions/default/1624279676213_alter_table_public_experiments_tags_add_column_value/down.sql
695Viewed
696@@ -0,0 +1,4 @@
697-- Could not auto-generate a down migration.
698-- Please write an appropriate down migration for the SQL below:
699-- alter table "public"."experiments_tags" add column "value" text
700 null;
7012 ...rations/default/1624279676213_alter_table_public_experiments_tags_add_column_value/up.sql
702Viewed
703@@ -0,0 +1,2 @@
704alter table "public"."experiments_tags" add column "value" text
705 null;
7065 .../migrations/default/1624279971048_delete_fk_public_tag_unit_tag_unit_tag_id_fkey/down.sql
707Viewed
708@@ -0,0 +1,5 @@
709alter table "public"."tag_unit"
710 add constraint "tag_unit_tag_id_fkey"
711 foreign key ("tag_id")
712 references "public"."tags"
713 ("id") on update restrict on delete restrict;
7141 ...ra/migrations/default/1624279971048_delete_fk_public_tag_unit_tag_unit_tag_id_fkey/up.sql
715Viewed
716@@ -0,0 +1 @@
717alter table "public"."tag_unit" drop constraint "tag_unit_tag_id_fkey";
7181 ...migrations/default/1624279988507_alter_table_public_tag_unit_alter_column_tag_id/down.sql
719Viewed
720@@ -0,0 +1 @@
721alter table "public"."tag_unit" rename column "workspace_id" to "tag_id";
7221 ...a/migrations/default/1624279988507_alter_table_public_tag_unit_alter_column_tag_id/up.sql
723Viewed
724@@ -0,0 +1 @@
725alter table "public"."tag_unit" rename column "tag_id" to "workspace_id";
7261 api/hasura/migrations/default/1624280054310_set_fk_public_tag_unit_workspace_id/down.sql
727Viewed
728@@ -0,0 +1 @@
729alter table "public"."tag_unit" drop constraint "tag_unit_workspace_id_fkey";
7305 api/hasura/migrations/default/1624280054310_set_fk_public_tag_unit_workspace_id/up.sql
731Viewed
732@@ -0,0 +1,5 @@
733alter table "public"."tag_unit"
734 add constraint "tag_unit_workspace_id_fkey"
735 foreign key ("workspace_id")
736 references "public"."workspaces"
737 ("id") on update restrict on delete restrict;
7384 ...a/migrations/default/1624280459309_alter_table_public_tags_add_column_is_deleted/down.sql
739Viewed
740@@ -0,0 +1,4 @@
741-- Could not auto-generate a down migration.
742-- Please write an appropriate down migration for the SQL below:
743-- alter table "public"."tags" add column "is_deleted" boolean
744 not null default 'false';
7452 ...ura/migrations/default/1624280459309_alter_table_public_tags_add_column_is_deleted/up.sql
746Viewed
747@@ -0,0 +1,2 @@
748alter table "public"."tags" add column "is_deleted" boolean
749 not null default 'false';
7504 ...grations/default/1624281065529_alter_table_public_tag_unit_add_column_is_deleted/down.sql
751Viewed
752@@ -0,0 +1,4 @@
753-- Could not auto-generate a down migration.
754-- Please write an appropriate down migration for the SQL below:
755-- alter table "public"."tag_unit" add column "is_deleted" boolean
756 not null default 'false';
7572 ...migrations/default/1624281065529_alter_table_public_tag_unit_add_column_is_deleted/up.sql
758Viewed
759@@ -0,0 +1,2 @@
760alter table "public"."tag_unit" add column "is_deleted" boolean
761 not null default 'false';
7624 ...ra/migrations/default/1624368775402_alter_table_public_tags_add_column_author_id/down.sql
763Viewed
764@@ -0,0 +1,4 @@
765-- Could not auto-generate a down migration.
766-- Please write an appropriate down migration for the SQL below:
767-- alter table "public"."tags" add column "author_id" uuid
768 not null;
7692 ...sura/migrations/default/1624368775402_alter_table_public_tags_add_column_author_id/up.sql
770Viewed
771@@ -0,0 +1,2 @@
772alter table "public"."tags" add column "author_id" uuid
773 not null;
7744 ...hasura/migrations/default/1624425546352_alter_table_public_tags_add_column_color/down.sql
775Viewed
776@@ -0,0 +1,4 @@
777-- Could not auto-generate a down migration.
778-- Please write an appropriate down migration for the SQL below:
779-- alter table "public"."tags" add column "color" text
780 not null;
7812 api/hasura/migrations/default/1624425546352_alter_table_public_tags_add_column_color/up.sql
782Viewed
783@@ -0,0 +1,2 @@
784alter table "public"."tags" add column "color" text
785 not null;
7861 api/hasura/migrations/default/1624426333648_set_fk_public_tags_author_id/down.sql
787Viewed
788@@ -0,0 +1 @@
789alter table "public"."tags" drop constraint "tags_author_id_fkey";
7905 api/hasura/migrations/default/1624426333648_set_fk_public_tags_author_id/up.sql
791Viewed
792@@ -0,0 +1,5 @@
793alter table "public"."tags"
794 add constraint "tags_author_id_fkey"
795 foreign key ("author_id")
796 references "public"."users"
797 ("id") on update restrict on delete restrict;
7981 api/hasura/migrations/default/1624429874996_set_fk_public_tags_default_unit/down.sql
799Viewed
800@@ -0,0 +1 @@
801alter table "public"."tags" drop constraint "tags_default_unit_fkey";
8025 api/hasura/migrations/default/1624429874996_set_fk_public_tags_default_unit/up.sql
803Viewed
804@@ -0,0 +1,5 @@
805alter table "public"."tags"
806 add constraint "tags_default_unit_fkey"
807 foreign key ("default_unit")
808 references "public"."tag_unit"
809 ("id") on update restrict on delete restrict;
81018 api/scripts/generate-hasura-sdk/methods.ts
811Viewed
812@@ -119,7 +119,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
813 }
814 `
815
816 return request(query, options) as unknown as T['aggregate']
817 return (request(query, options) as unknown) as T['aggregate']
818 }
819
820 const find = async ({
821@@ -140,7 +140,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
822 }
823 `
824
825 return request(query, pks) as unknown as T['find']
826 return (request(query, pks) as unknown) as T['find']
827 }
828
829 const findMany = async ({
830@@ -161,7 +161,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
831 }
832 `
833
834 return request(query, options) as unknown as T['findMany'][]
835 return (request(query, options) as unknown) as T['findMany'][]
836 }
837
838 const insert = async ({
839@@ -176,7 +176,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
840 }
841 `
842
843 return request(mutation, options) as unknown as T['insert']
844 return (request(mutation, options) as unknown) as T['insert']
845 }
846
847 const insertMany = async ({
848@@ -191,7 +191,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
849 }
850 `
851
852 return request(mutation, options) as unknown as T['insertMany']
853 return (request(mutation, options) as unknown) as T['insertMany']
854 }
855
856 const update = async ({
857@@ -212,7 +212,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
858 }
859 `
860
861 return request(mutation, options) as unknown as T['update']
862 return (request(mutation, options) as unknown) as T['update']
863 }
864
865 const updateMany = async ({
866@@ -227,7 +227,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
867 }
868 `
869
870 return request(mutation, options) as unknown as T['updateMany']
871 return (request(mutation, options) as unknown) as T['updateMany']
872 }
873
874 const deleteRow = async ({
875@@ -248,7 +248,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
876 }
877 `
878
879 return request(mutation, pks) as unknown as T['delete']
880 return (request(mutation, pks) as unknown) as T['delete']
881 }
882
883 const deleteMany = async ({
884@@ -263,7 +263,7 @@ const generateModel = <T extends OptionsMap, O extends OptionsMap>({
885 }
886 `
887
888 return request(mutation, options) as unknown as T['deleteMany']
889 return (request(mutation, options) as unknown) as T['deleteMany']
890 }
891
892 return {
89377 api/src/functions/actions/create-tag/index.ts
894Viewed
895@@ -0,0 +1,77 @@
896import { gql } from 'graphql-request'
897import { restAdapter } from '@utils/middleware'
898import { request } from '@utils/gql'
899import { checkPermissions, isEditor } from '@shared/permissions'
900
901const INSERT_TAG = gql`
902 mutation InsertTag($payload: tags_insert_input!) {
903 insert_tags_one(object: $payload) {
904 id
905 name
906 default_unit
907 default_value
908 color
909 author {
910 id
911 name
912 }
913 workspace {
914 id
915 name
916 slug
917 }
918 }
919 }
920`
921
922const GET_WORKSPACE = gql`
923 query Workspace($id: uuid!) {
924 workspaces_by_pk(id: $id) {
925 id
926 author {
927 id
928 }
929 projects {
930 name
931 id
932 }
933 collaborators {
934 role
935 invited_for_projects
936 collaborator_id
937 collaborator {
938 email
939 }
940 }
941 }
942 }
943`
944
945const findWorkspace = id => {
946 return request(GET_WORKSPACE, { id })
947}
948
949const insertTag = payload => request(INSERT_TAG, { payload })
950
951export default restAdapter(async ({ body }) => {
952 const { name, default_unit, color, workspace_id } = body.input.payload || {}
953 const userId = body.session_variables['x-hasura-user-id'] as string
954
955 const workspace = await findWorkspace(workspace_id)
956
957 if (!checkPermissions(workspace, userId, isEditor)) {
958 throw new Error('Failed to invite to project: insufficient permissions')
959 }
960
961 const tag = await insertTag({
962 name,
963 author_id: userId,
964 default_unit,
965 workspace_id,
966 color,
967 })
968
969 return {
970 data: { tag },
971 }
972})
97374 api/src/functions/actions/create-template/index.ts
974Viewed
975@@ -0,0 +1,74 @@
976import { gql } from 'graphql-request'
977import { restAdapter } from '@utils/middleware'
978import { request } from '@utils/gql'
979import { checkPermissions, isEditor } from '@shared/permissions'
980
981const INSERT_TEMPLATE = gql`
982 mutation InsertTemplate($payload: templates_insert_input!) {
983 insert_templates_one(object: $payload) {
984 id
985 name
986 summary
987 author {
988 id
989 name
990 }
991 workspace {
992 id
993 name
994 slug
995 }
996 }
997 }
998`
999
1000const GET_WORKSPACE = gql`
1001 query Workspace($id: uuid!) {
1002 workspaces_by_pk(id: $id) {
1003 id
1004 author {
1005 id
1006 }
1007 projects {
1008 name
1009 id
1010 }
1011 collaborators {
1012 role
1013 invited_for_projects
1014 collaborator_id
1015 collaborator {
1016 email
1017 }
1018 }
1019 }
1020 }
1021`
1022
1023const findWorkspace = id => {
1024 return request(GET_WORKSPACE, { id })
1025}
1026
1027const insertTemplate = payload => request(INSERT_TEMPLATE, { payload })
1028
1029export default restAdapter(async ({ body }) => {
1030 const { name, summary, workspace_id } = body.input.payload || {}
1031 const userId = body.session_variables['x-hasura-user-id'] as string
1032
1033 const workspace = await findWorkspace(workspace_id)
1034
1035 if (!checkPermissions(workspace, userId, isEditor)) {
1036 throw new Error('Failed to invite to project: insufficient permissions')
1037 }
1038
1039 const template = await insertTemplate({
1040 name,
1041 summary,
1042 author_id: userId,
1043 workspace_id,
1044 })
1045
1046 return {
1047 data: { template },
1048 }
1049})
105040 api/src/functions/events/initialize-workspace/index.ts
1051Viewed
1052@@ -31,6 +31,10 @@ const insertWorkspaceCollaborator = payload =>
1053
1054const insertProject = payload => request(INSERT_PROJECT, { payload })
1055
1056const insertTemplate = payload => request(INSERT_TEMPLATE, { payload })
1057
1058const insertTagUnits = payload => request(INSERT_UNITS, { payload })
1059
1060const INSERT_PROJECT = gql`
1061 mutation insert_project($payload: projects_insert_input!) {
1062 insert_projects_one(object: $payload) {
1063@@ -39,6 +43,28 @@ const INSERT_PROJECT = gql`
1064 }
1065`
1066
1067const INSERT_TEMPLATE = gql`
1068 mutation insert_template($payload: templates_insert_input!) {
1069 insert_templates_one(object: $payload) {
1070 id
1071 }
1072 }
1073`
1074
1075const INSERT_UNITS = gql`
1076 mutation insert_units($payload: [tag_unit_insert_input!]!) {
1077 insert_tag_unit(objects: $payload) {
1078 affected_rows
1079 }
1080 }
1081`
1082
1083const basicTemplate = {
1084 name: 'Basic template',
1085 summary:
1086 '<h1>Purpose</h1> <p></p> <h1>Equipment</h1> <ul> <li></li> <li></li> </ul> <h1>Materials</h1> <ul> <li></li> <li></li> </ul> <h1>Procedure</h1> <h2>Sub-procedure 1</h2> <ul data-type="todo_list"> <li data-type="todo_item" data-done="false"></li> <li data-type="todo_item" data-done="false"></li> </ul> <h2>Sub-procedure 2</h2> <ul data-type="todo_list"> <li data-type="todo_item" data-done="false"></li> <li data-type="todo_item" data-done="false"></li> </ul> <h1>Results</h1> <p></p>',
1087}
1088
1089export default restAdapter(async ({ body }) => {
1090 const { id: workspace_id, author_id } = body.event.data.new
1091
1092@@ -53,6 +79,20 @@ export default restAdapter(async ({ body }) => {
1093 // Create an entity to store workspace preferences
1094 await insertWorkspacePreferences({ workspace_id })
1095
1096 // Create basic template
1097 insertTemplate({
1098 ...basicTemplate,
1099 author_id,
1100 workspace_id,
1101 })
1102
1103 // Create basic tag units
1104 insertTagUnits([
1105 { unit: 'Text', workspace_id },
1106 { unit: '%', workspace_id },
1107 { unit: 'mL', workspace_id },
1108 ])
1109
1110 // An example project to fill the space and help users become familiar with Colabra
1111 await insertProject({
1112 workspace_id,
111324 src/components/ColorPicker.vue
1114Viewed
1115@@ -0,0 +1,24 @@
1116<template>
1117 <div class="d-flex">
1118 <v-btn v-for="color in colors" :key="color" icon @click="pick(color)">
1119 <v-icon :color="color">mdi-circle</v-icon>
1120 </v-btn>
1121 </div>
1122</template>
1123
1124<script>
1125import { colors } from '@constants/maps'
1126
1127export default {
1128 data() {
1129 return {
1130 colors,
1131 }
1132 },
1133 methods: {
1134 pick(color) {
1135 this.$emit('action', color)
1136 },
1137 },
1138}
1139</script>
11403 src/components/EditableName.vue
1141Viewed
1142@@ -11,7 +11,7 @@
1143 v-model.trim="name"
1144 class="pt-0 black--text"
1145 autofocus
1146 placeholder="The Sourdough Project"
1147 :placeholder="placeholder"
1148 :error-messages="formErrors.name"
1149 @change="update"
1150 @focus="$v.name.$touch()"
1151@@ -44,6 +44,7 @@ export default {
1152 props: {
1153 value: { type: String },
1154 disabled: { type: Boolean },
1155 placeholder: { type: String, default: 'Template name' },
1156 },
1157 data: () => ({
1158 editing: false,
11592 src/components/SettingsModal/SideBar.vue
1160Viewed
1161@@ -63,6 +63,8 @@ export default {
1162 { title: 'General', icon: 'mdi-hexagon-multiple-outline', key: 'workspace-general' },
1163 { title: 'Members', icon: 'mdi-account-multiple-outline', key: 'workspace-members' },
1164 { title: 'Security', icon: 'mdi-shield-lock-outline', key: 'workspace-security' },
1165 { title: 'Tags', icon: 'mdi-tag-outline', key: 'workspace-tags' },
1166 { title: 'Templates', icon: 'mdi-file-hidden', key: 'workspace-templates' },
1167 ]
1168 },
1169 },
11706 src/components/SettingsModal/index.vue
1171Viewed
1172@@ -33,6 +33,8 @@ import SideBar from './SideBar.vue'
1173import WorkspaceGeneral from './workspace/General.vue'
1174import WorkspaceMembers from './workspace/Members.vue'
1175import WorkspaceSecurity from './workspace/Security.vue'
1176import WorkspaceTags from './workspace/Tags/index.vue'
1177import WorkspaceTemplates from './workspace/Templates/index.vue'
1178
1179export default {
1180 name: 'Settings',
1181@@ -44,6 +46,8 @@ export default {
1182 WorkspaceGeneral,
1183 WorkspaceMembers,
1184 WorkspaceSecurity,
1185 WorkspaceTags,
1186 WorkspaceTemplates,
1187 },
1188 data: () => ({
1189 user: {},
1190@@ -57,6 +61,8 @@ export default {
1191 'workspace-general',
1192 'workspace-members',
1193 'workspace-security',
1194 'workspace-tags',
1195 'workspace-templates',
1196 ],
1197 }),
1198 apollo: {
1199177 src/components/SettingsModal/workspace/Tags/TagForm.vue
1200Viewed
1201@@ -0,0 +1,177 @@
1202<template>
1203 <div
1204 class="d-flex"
1205 :class="{
1206 'my-3': isEdit,
1207 }"
1208 >
1209 <div class="d-flex mr-6">
1210 <!-- Choose color -->
1211 <v-menu
1212 v-model="chooseColor"
1213 left
1214 bottom
1215 transition="slide-y-transition"
1216 :offset-y="true"
1217 min-width="400"
1218 max-height="600"
1219 >
1220 <!-- Menu button with workspace logo and -->
1221 <template v-slot:activator="{ on, attrs }">
1222 <v-btn
1223 v-bind="attrs"
1224 class="color-btn mr-2 cx-box-shadow"
1225 outlined
1226 color="white"
1227 v-on="on"
1228 >
1229 <v-icon :color="value.color"> mdi-circle-medium </v-icon>
1230 </v-btn>
1231 </template>
1232
1233 <ColorPicker
1234 @click="chooseColor = false"
1235 @action="value.color = $event"
1236 />
1237 </v-menu>
1238
1239 <!-- Enter name -->
1240 <v-text-field
1241 v-model="value.name"
1242 class="col-6 cx-text-field"
1243 label="Tag label"
1244 solo
1245 hide-details
1246 dense
1247 />
1248
1249 <!-- Type -->
1250 <CxSelect
1251 v-model="value.default_unit"
1252 class="ml-2"
1253 :items="types"
1254 item-text="unit"
1255 item-value="id"
1256 label="Type"
1257 hide-details
1258 append-icon="mdi-chevron-down"
1259 />
1260 </div>
1261
1262 <v-btn
1263 v-if="isEdit"
1264 color="primary"
1265 outlined
1266 depressed
1267 class="mr-2 text-none"
1268 @click="$emit('cancel')"
1269 >
1270 Cancel
1271 </v-btn>
1272
1273 <!-- New template btn -->
1274 <v-btn
1275 color="primary"
1276 medium
1277 depressed
1278 type="submit"
1279 class="text-none"
1280 :loading="loading[isEdit ? 'newTag' : 'editTag']"
1281 @click="click"
1282 >
1283 {{ isEdit ? 'Save' : 'Add tag' }}
1284 </v-btn>
1285 </div>
1286</template>
1287
1288<script>
1289import ColorPicker from '@components/ColorPicker.vue'
1290import apollo from '@mixins/apollo'
1291import { CREATE_TAG, EDIT_TAG_BY_ID } from '@state/gql/tags'
1292
1293const initialData = {
1294 color: '#ce7d78',
1295 name: '',
1296 default_unit: null,
1297}
1298
1299export default {
1300 components: {
1301 ColorPicker,
1302 },
1303 mixins: [apollo],
1304 props: {
1305 isEdit: { type: Boolean },
1306 tag: { type: Object },
1307 workspace: { type: Object },
1308 },
1309 data() {
1310 return {
1311 value: this.tag || { ...initialData },
1312 chooseColor: false,
1313 }
1314 },
1315 computed: {
1316 types() {
1317 return [{ id: null, unit: 'No value' }, ...this.workspace.tag_units]
1318 },
1319 },
1320 methods: {
1321 click() {
1322 if (this.isEdit) {
1323 this.editTag()
1324 } else {
1325 this.createTag()
1326 }
1327 },
1328 async createTag() {
1329 await this.$mutate({
1330 key: 'newTag',
1331 mutation: CREATE_TAG,
1332 variables: {
1333 payload: {
1334 ...this.value,
1335 workspace_id: this.workspace.id,
1336 },
1337 },
1338 })
1339
1340 this.value = { ...initialData }
1341 },
1342 async editTag() {
1343 const { id, name, color, default_unit } = this.value
1344
1345 await this.$mutate({
1346 key: 'editTag',
1347 mutation: EDIT_TAG_BY_ID,
1348 variables: {
1349 id,
1350 payload: {
1351 name,
1352 color,
1353 default_unit,
1354 },
1355 },
1356 })
1357
1358 const payload = {
1359 ...this.value,
1360 tag_unit: {
1361 ...this.types.find(x => x.id === this.value.default_unit),
1362 },
1363 }
1364
1365 this.$emit('save', payload)
1366 },
1367 },
1368}
1369</script>
1370
1371<style lang="scss" scoped>
1372.color-btn {
1373 min-width: 36px !important;
1374 padding: 0 !important;
1375 border: 1px solid #e2e2e2 !important;
1376 box-shadow: 0 1px 1px rgba(0, 0, 0, 0.09) !important;
1377}
1378</style>
1379103 src/components/SettingsModal/workspace/Tags/TagItem.vue
1380Viewed
1381@@ -0,0 +1,103 @@
1382<template>
1383 <div>
1384 <ConfirmationDialog
1385 v-if="remove.dialog"
1386 :dialog="remove.dialog"
1387 title="Delete tag?"
1388 :text="'Are you sure you want to permanently delete the tag?'"
1389 action-text="Delete"
1390 @close="remove.dialog = false"
1391 @action="removeTag"
1392 />
1393
1394 <!-- Display mode -->
1395 <div v-if="!editMode" class="d-flex justify-space-between">
1396 <!-- Name & default unit -->
1397 <div class="d-flex align-center">
1398 <v-icon :color="value.color">mdi-circle-medium</v-icon>
1399 <span class="font-weight-bold mr-8">{{ value.name }}</span>
1400 <span class="text--disabled">{{
1401 value.default_unit ? value.tag_unit.unit : 'Tag has no value'
1402 }}</span>
1403 </div>
1404
1405 <!-- edit, remove btns -->
1406 <div class="d-flex align-center">
1407 <!-- Count linked experiments -->
1408 <span class="text--disabled mr-4">{{
1409 `${count} ${count.length === 1 ? 'experiment' : 'experiments'}`
1410 }}</span>
1411
1412 <!-- Edit tag -->
1413 <v-btn icon class="mr-1" @click="editMode = true">
1414 <v-icon>mdi-pencil-outline</v-icon>
1415 </v-btn>
1416
1417 <!-- Remove tag -->
1418 <v-btn icon :loading="loading.removeTag" @click="remove.dialog = true">
1419 <v-icon>mdi-delete-outline</v-icon>
1420 </v-btn>
1421 </div>
1422 </div>
1423
1424 <!-- Edit mode -->
1425 <div v-else>
1426 <TagForm
1427 isEdit
1428 :tag="value"
1429 :workspace="workspace"
1430 @cancel="editMode = false"
1431 @save="update"
1432 />
1433 </div>
1434 </div>
1435</template>
1436
1437<script>
1438import ConfirmationDialog from '@components/ConfirmationDialog.vue'
1439import apollo from '@mixins/apollo'
1440import { REMOVE_TAG } from '@state/gql/tags'
1441
1442import TagForm from './TagForm.vue'
1443
1444export default {
1445 components: {
1446 ConfirmationDialog,
1447 TagForm,
1448 },
1449 mixins: [apollo],
1450 props: {
1451 tag: { type: Object, required: true },
1452 workspace: { type: Object, required: true },
1453 },
1454 data() {
1455 return {
1456 remove: {
1457 dialog: false,
1458 },
1459 editMode: false,
1460 value: this.tag,
1461 }
1462 },
1463 computed: {
1464 count() {
1465 return this.tag.experiments_tags_aggregate.aggregate.count
1466 },
1467 },
1468 methods: {
1469 async removeTag() {
1470 await this.$mutate({
1471 key: 'removeTag',
1472 mutation: REMOVE_TAG,
1473 variables: {
1474 id: this.tag.id,
1475 },
1476 })
1477 },
1478 update(newData) {
1479 this.value = newData
1480 this.editMode = false
1481 },
1482 },
1483}
1484</script>
148555 src/components/SettingsModal/workspace/Tags/index.vue
1486Viewed
1487@@ -0,0 +1,55 @@
1488<template>
1489 <div v-if="workspace">
1490 <div class="col-md-11 col-sm-12 pa-0">
1491 <!-- Header -->
1492 <div>
1493 <h2 class="mb-2">Tags</h2>
1494 <p>
1495 Tags let you augment experiments with metadata for search and
1496 meta-analysis.
1497 </p>
1498 </div>
1499
1500 <!-- New tag form -->
1501 <TagForm :workspace="workspace" />
1502
1503 <!-- Templates -->
1504 <div class="mt-3">
1505 <div v-for="(item, index) in workspace.tags" :key="item.id">
1506 <TagItem :tag="item" :workspace="workspace" />
1507
1508 <!-- Insert divider if it's not the last item -->
1509 <v-divider
1510 v-if="index < workspace.tags.length - 1"
1511 :key="index"
1512 class="my-1"
1513 />
1514 </div>
1515 </div>
1516 </div>
1517 </div>
1518</template>
1519
1520<script>
1521import TagForm from './TagForm.vue'
1522import TagItem from './TagItem.vue'
1523
1524export default {
1525 name: 'WorkspaceTemplates',
1526 components: {
1527 TagItem,
1528 TagForm,
1529 },
1530 props: {
1531 user: { type: Object },
1532 workspace: { type: Object },
1533 canEdit: { type: Boolean },
1534 },
1535}
1536</script>
1537
1538<style lang="scss" scoped>
1539.v-list-item__title {
1540 white-space: normal;
1541}
1542</style>
154377 src/components/SettingsModal/workspace/Templates/TemplateItem.vue
1544Viewed
1545@@ -0,0 +1,77 @@
1546<template>
1547 <div class="d-flex justify-space-between">
1548 <ConfirmationDialog
1549 v-if="remove.dialog"
1550 :dialog="remove.dialog"
1551 title="Delete template?"
1552 :text="'Are you sure you want to permanently delete the template?'"
1553 action-text="Delete"
1554 @close="remove.dialog = false"
1555 @action="removeTemplate"
1556 />
1557 <!-- Name & created date -->
1558 <div class="d-flex align-center">
1559 <span class="font-weight-bold mr-8">{{ template.name }}</span>
1560 <span class="text--disabled">{{
1561 $luxon(template.created_at, 'relative')
1562 }}</span>
1563 </div>
1564
1565 <!-- Author & edit, remove btns -->
1566 <div class="d-flex align-center">
1567 <!-- Author name -->
1568 <span class="text--disabled mr-4">{{ template.author.name }}</span>
1569
1570 <!-- Edit template -->
1571 <v-btn icon class="mr-1" @click="edit">
1572 <v-icon>mdi-pencil-outline</v-icon>
1573 </v-btn>
1574
1575 <!-- Remove template -->
1576 <v-btn
1577 icon
1578 :loading="loading.removeTemplate"
1579 @click="remove.dialog = true"
1580 >
1581 <v-icon>mdi-delete-outline</v-icon>
1582 </v-btn>
1583 </div>
1584 </div>
1585</template>
1586
1587<script>
1588import ConfirmationDialog from '@components/ConfirmationDialog.vue'
1589import apollo from '@mixins/apollo'
1590import { REMOVE_TEMPLATE } from '@state/gql/template'
1591
1592export default {
1593 components: {
1594 ConfirmationDialog,
1595 },
1596 mixins: [apollo],
1597 props: {
1598 template: { type: Object, required: true },
1599 },
1600 data() {
1601 return {
1602 remove: {
1603 dialog: false,
1604 },
1605 }
1606 },
1607 methods: {
1608 edit() {
1609 this.$goToTemplate(this.template)
1610 },
1611 async removeTemplate() {
1612 await this.$mutate({
1613 key: 'removeTemplate',
1614 mutation: REMOVE_TEMPLATE,
1615 variables: {
1616 id: this.template.id,
1617 },
1618 })
1619 },
1620 },
1621}
1622</script>
162378 src/components/SettingsModal/workspace/Templates/index.vue
1624Viewed
1625@@ -0,0 +1,78 @@
1626<template>
1627 <div v-if="workspace">
1628 <div class="col-md-11 col-sm-12 pa-0">
1629 <!-- Header -->
1630 <div>
1631 <h2 class="mb-2">Templates</h2>
1632 <p>
1633 Jumpstart new experiments with a title, summary, assignee and tags.
1634 </p>
1635 </div>
1636
1637 <!-- Templates -->
1638 <div>
1639 <div v-for="(item, index) in workspace.templates" :key="item.id">
1640 <TemplateItem :template="item" />
1641
1642 <!-- Insert divider if it's not the last item -->
1643 <v-divider
1644 v-if="index < workspace.templates.length - 1"
1645 :key="index"
1646 class="my-1"
1647 />
1648 </div>
1649 </div>
1650
1651 <!-- New template btn -->
1652 <v-btn
1653 color="primary"
1654 medium
1655 depressed
1656 type="submit"
1657 class="px-6 text-none mt-3"
1658 :loading="loading.newTemplate"
1659 @click="createTemplate"
1660 >
1661 New template
1662 </v-btn>
1663 </div>
1664 </div>
1665</template>
1666
1667<script>
1668import apollo from '@mixins/apollo'
1669import { CREATE_TEMPLATE } from '@state/gql/template'
1670
1671import TemplateItem from './TemplateItem'
1672
1673export default {
1674 name: 'WorkspaceTemplates',
1675 components: {
1676 TemplateItem,
1677 },
1678 mixins: [apollo],
1679 props: {
1680 user: { type: Object },
1681 workspace: { type: Object },
1682 canEdit: { type: Boolean },
1683 },
1684 methods: {
1685 async createTemplate() {
1686 const { template } = await this.$mutate({
1687 key: 'newTemplate',
1688 mutation: CREATE_TEMPLATE,
1689 variables: {
1690 workspace_id: this.workspace.id,
1691 },
1692 })
1693
1694 this.$goToTemplate(template)
1695 },
1696 },
1697}
1698</script>
1699
1700<style lang="sass" scoped>
1701.v-list-item__title
1702 white-space: normal
1703</style>
170418 src/constants/maps.js
1705Viewed
1706@@ -254,3 +254,21 @@ export const notificationsTabs = [
1707 { id: 1, title: 'All' },
1708 { id: 2, title: 'Read' },
1709]
1710
1711export const colors = [
1712 '#C0C0C0',
1713 '#808080',
1714 '#000000',
1715 '#FF0000',
1716 '#800000',
1717 '#FFFF00',
1718 '#808000',
1719 '#00FF00',
1720 '#008000',
1721 '#00FFFF',
1722 '#008080',
1723 '#0000FF',
1724 '#000080',
1725 '#FF00FF',
1726 '#800080',
1727]
172811 src/mixins/navigation.js
1729Viewed
1730@@ -110,5 +110,16 @@ export default Vue.extend({
1731 ...options,
1732 })
1733 },
1734
1735 $goToTemplate(template, options) {
1736 const workspaceSlug =
1737 template?.workspace?.slug || this.$route.params.workspaceSlug
1738
1739 return this.$goTo({
1740 name: 'Template',
1741 params: { workspaceSlug, templateId: template?.id },
1742 ...options,
1743 })
1744 },
1745 },
1746})
17476 src/router/index.js
1748Viewed
1749@@ -40,6 +40,12 @@ const routes = [
1750 component: () => import('../views/Invitation.vue'),
1751 props: true,
1752 },
1753 {
1754 path: '/:workspaceSlug/template/:templateId',
1755 name: 'Template',
1756 component: () => import('../views/Template.vue'),
1757 props: true,
1758 },
1759 {
1760 path: '/scans',
1761 name: 'Scans',
176219 src/state/gql/settings.js
1763Viewed
1764@@ -1,5 +1,7 @@
1765import gql from 'graphql-tag'
1766
1767import { TagDataFragment } from './tags'
1768
1769export const UserPreferencesFragment = gql`
1770 fragment UserPreferences on user_preferences {
1771 default_view
1772@@ -36,9 +38,25 @@ export const USER_SETTINGS = gql`
1773 id
1774 name
1775 slug
1776 tags(order_by: { created_at: desc }) {
1777 ...TagData
1778 }
1779 tag_units {
1780 id
1781 unit
1782 }
1783 preferences {
1784 ...WorkspacePreferences
1785 }
1786 templates {
1787 id
1788 name
1789 summary
1790 created_at
1791 author {
1792 name
1793 }
1794 }
1795 collaborators {
1796 collaborator_id
1797 invited_for_projects
1798@@ -60,6 +78,7 @@ export const USER_SETTINGS = gql`
1799 }
1800 ${UserPreferencesFragment}
1801 ${WorkspacePreferencesFragment}
1802 ${TagDataFragment}
1803`
1804
1805export const UPDATE_USER_PREFERENCES = gql`
180647 src/state/gql/tags.js
1807Viewed
1808@@ -0,0 +1,47 @@
1809import gql from 'graphql-tag'
1810
1811export const TagDataFragment = gql`
1812 fragment TagData on tags {
1813 id
1814 name
1815 default_unit
1816 default_value
1817 color
1818 tag_unit {
1819 unit
1820 }
1821 experiments_tags_aggregate {
1822 aggregate {
1823 count
1824 }
1825 }
1826 author {
1827 id
1828 name
1829 }
1830 }
1831`
1832
1833export const EDIT_TAG_BY_ID = gql`
1834 mutation editTag($id: uuid!, $payload: tags_set_input!) {
1835 update_tags_by_pk(pk_columns: { id: $id }, _set: $payload) {
1836 id
1837 }
1838 }
1839`
1840
1841export const REMOVE_TAG = gql`
1842 mutation remove_tags($id: uuid!) {
1843 update_tags_by_pk(pk_columns: { id: $id }, _set: { is_deleted: true }) {
1844 id
1845 }
1846 }
1847`
1848
1849export const CREATE_TAG = gql`
1850 mutation createTag($payload: create_tag_input!) {
1851 create_tag(payload: $payload) {
1852 tag
1853 }
1854 }
1855`
185653 src/state/gql/template.js
1857Viewed
1858@@ -0,0 +1,53 @@
1859import gql from 'graphql-tag'
1860
1861export const TemplateDataFragment = gql`
1862 fragment TemplateData on templates {
1863 id
1864 name
1865 summary
1866 created_at
1867 author {
1868 name
1869 }
1870 workspace {
1871 name
1872 slug
1873 }
1874 }
1875`
1876
1877export const GET_TEMPLATE_BY_ID = gql`
1878 query templateDetail($id: uuid!) {
1879 template: templates_by_pk(id: $id) {
1880 ...TemplateData
1881 }
1882 }
1883 ${TemplateDataFragment}
1884`
1885
1886export const EDIT_TEMPLATE_BY_ID = gql`
1887 mutation editTemplate($id: uuid!, $payload: templates_set_input!) {
1888 update_templates_by_pk(pk_columns: { id: $id }, _set: $payload) {
1889 id
1890 }
1891 }
1892`
1893
1894export const REMOVE_TEMPLATE = gql`
1895 mutation removeTemplate($id: uuid!) {
1896 update_templates_by_pk(
1897 pk_columns: { id: $id }
1898 _set: { is_deleted: true }
1899 ) {
1900 id
1901 }
1902 }
1903`
1904
1905export const CREATE_TEMPLATE = gql`
1906 mutation createTemplate($workspace_id: uuid!) {
1907 create_template(payload: { workspace_id: $workspace_id }) {
1908 template
1909 }
1910 }
1911`
19124 src/state/gql/user.js
1913Viewed
1914@@ -69,6 +69,10 @@ export const UserPrivateFragment = gql`
1915 id
1916 name
1917 slug
1918 tag_units {
1919 id
1920 unit
1921 }
1922 collaborators {
1923 collaborator_id
1924 role
192521 src/state/gql/workspace.js
1926Viewed
1927@@ -23,6 +23,19 @@ export const WorkspaceDataFragment = gql`
1928 email
1929 }
1930 }
1931 tag_units {
1932 id
1933 unit
1934 }
1935 templates {
1936 id
1937 name
1938 summary
1939 created_at
1940 author {
1941 name
1942 }
1943 }
1944 collaborators(order_by: { invited_for_projects: asc }) {
1945 invite_email
1946 role
1947@@ -48,6 +61,10 @@ export const WorkspaceDataWithProjectsFragment = gql`
1948 updated_at
1949 is_deleted
1950 slug
1951 tag_units {
1952 id
1953 unit
1954 }
1955 private_projects(
1956 order_by: { private_collaborator: { last_viewed: desc } }
1957 ) {
1958@@ -91,6 +108,10 @@ export const PrivateWorkspaceDataFragment = gql`
1959 email
1960 }
1961 }
1962 tag_units {
1963 id
1964 unit
1965 }
1966 collaborators {
1967 invite_email
1968 role
19693 src/styles/helpers.scss
1970Viewed
1971@@ -4,3 +4,6 @@
1972.cursor-pointer {
1973 cursor: pointer;
1974}
1975.cx-box-shadow {
1976 box-shadow: 0 1px 1px rgb(0 0 0 / 9%) !important;
1977}
1978149 src/views/Template.vue
1979Viewed
1980@@ -0,0 +1,149 @@
1981<template>
1982 <div>
1983 <v-col class="col-lg-10 offset-lg-1 px-0">
1984 <transition name="slide-up" appear>
1985 <v-card v-if="template" class="mx-auto px-4 pb-2">
1986 <!-- Name -->
1987 <v-list>
1988 <v-list-item
1989 class="pt-0"
1990 :class="{
1991 'px-2': $vuetify.breakpoint.smAndDown,
1992 }"
1993 >
1994 <!-- // TODO: smooth -->
1995 <v-list-item-content
1996 v-if="template"
1997 class="d-flex justify-space-between"
1998 >
1999 <!-- Editable experiment name -->
2000 <EditableName
2001 :value="template.name"
2002 :disabled="loading.save"
2003 class="col-6 pl-0"
2004 @update="template.name = $event"
2005 />
2006 <div class="d-flex btns justify-end">
2007 <!-- Cancel -->
2008 <v-btn
2009 color="primary"
2010 medium
2011 depressed
2012 outlined
2013 type="submit"
2014 class="px-6 text-none mt-3 mr-2"
2015 @click="cancel"
2016 >
2017 Cancel
2018 </v-btn>
2019 <!-- Save template -->
2020 <v-btn
2021 color="primary"
2022 medium
2023 depressed
2024 type="submit"
2025 class="px-6 text-none mt-3"
2026 :loading="loading.save"
2027 @click="save"
2028 >
2029 Save template
2030 </v-btn>
2031 </div>
2032 </v-list-item-content>
2033 </v-list-item>
2034 </v-list>
2035
2036 <!-- Summary -->
2037 <div>
2038 <!-- Project summary -->
2039 <div class="black--text lighten-3">
2040 <TextEditor
2041 :text="template.summary"
2042 :autosave="true"
2043 :canEdit="true"
2044 :delay="0"
2045 placeholder="Summary"
2046 editable
2047 :resource="{ type: 'template', id: template.id }"
2048 :commands="allowedCommands"
2049 @update="template.summary = $event"
2050 />
2051 </div>
2052 </div>
2053 </v-card>
2054
2055 <!-- // TODO: smooth transition below -->
2056 <v-card v-else>
2057 <v-skeleton-loader
2058 type="card-heading, divider, list-item-three-line"
2059 />
2060 </v-card>
2061 </transition>
2062 </v-col>
2063 </div>
2064</template>
2065
2066<script>
2067import EditableName from '@components/EditableName.vue'
2068import TextEditor from '@components/TextEditor.vue'
2069import apollo from '@mixins/apollo'
2070import { EDIT_TEMPLATE_BY_ID, GET_TEMPLATE_BY_ID } from '@state/gql/template'
2071
2072export default {
2073 name: 'TemplateView',
2074 components: {
2075 EditableName,
2076 TextEditor,
2077 },
2078 mixins: [apollo],
2079 props: {
2080 templateId: String,
2081 },
2082 data() {
2083 return {
2084 template: null,
2085 allowedCommands: [0, 1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 15, 16, 17],
2086 }
2087 },
2088 apollo: {
2089 getTemplate: {
2090 query: GET_TEMPLATE_BY_ID,
2091 variables() {
2092 return {
2093 id: this.templateId,
2094 }
2095 },
2096 update(data) {
2097 this.template = data.template
2098 },
2099 },
2100 },
2101 computed: {},
2102 methods: {
2103 cancel() {
2104 this.$goToWorkspace(this.template.workspace)
2105 },
2106 async save() {
2107 await this.$mutate({
2108 key: 'save',
2109 mutation: EDIT_TEMPLATE_BY_ID,
2110 variables: {
2111 id: this.template.id,
2112 payload: {
2113 name: this.template.name,
2114 summary: this.template.summary,
2115 },
2116 },
2117 })
2118
2119 this.$goToWorkspace(this.template.workspace)
2120 },
2121 },
2122}
2123</script>
2124
2125<style lang="scss" scoped>
2126.btns {
2127 flex: auto;
2128}
2129</style>
2130ProTip! Use n and p to navigate between commits in a pull request.
2131
2132 © 2021 GitHub, Inc.
2133 Terms
2134 Privacy
2135 Security
2136 Status
2137 Docs
2138
2139 Contact GitHub
2140 Pricing
2141 API
2142 Training
2143 Blog
2144 About
2145
2146Loading complete