diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index c8490d807..5d1602395 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -5,6 +5,7 @@
from core.models import *
from iam.models import *
+from ebios_rm.models import EbiosRMStudy
from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied
@@ -600,6 +601,14 @@ class ComplianceAssessmentWriteSerializer(BaseModelSerializer):
required=False,
allow_null=True,
)
+ ebios_rm_studies = serializers.SlugRelatedField(
+ many=True,
+ slug_field="id",
+ queryset=EbiosRMStudy.objects.all(),
+ required=False,
+ allow_null=True,
+ write_only=True,
+ )
create_applied_controls_from_suggestions = serializers.BooleanField(
write_only=True, required=False, default=False
)
@@ -712,3 +721,13 @@ class FilteringLabelWriteSerializer(BaseModelSerializer):
class Meta:
model = FilteringLabel
exclude = ["folder", "is_published"]
+
+
+class QualificationReadSerializer(ReferentialSerializer):
+ class Meta:
+ model = Qualification
+ exclude = ["translations"]
+
+
+class QualificationWriteSerializer(QualificationReadSerializer):
+ pass
diff --git a/backend/core/startup.py b/backend/core/startup.py
index 37c06a1e5..3196379e2 100644
--- a/backend/core/startup.py
+++ b/backend/core/startup.py
@@ -363,6 +363,10 @@
"view_operationalscenario",
"change_operationalscenario",
"delete_operationalscenario",
+ "view_qualification",
+ "add_qualification",
+ "change_qualification",
+ "delete_qualification",
]
THIRD_PARTY_RESPONDENT_PERMISSIONS_LIST = [
diff --git a/backend/core/urls.py b/backend/core/urls.py
index 57b55901e..0b79a6c3c 100644
--- a/backend/core/urls.py
+++ b/backend/core/urls.py
@@ -66,6 +66,11 @@
FilteringLabelViewSet,
basename="filtering-labels",
)
+router.register(
+ r"qualifications",
+ QualificationViewSet,
+ basename="qualifications",
+)
ROUTES = settings.ROUTES
MODULES = settings.MODULES.values()
diff --git a/backend/core/views.py b/backend/core/views.py
index d7c825dd7..858cf7118 100644
--- a/backend/core/views.py
+++ b/backend/core/views.py
@@ -358,7 +358,13 @@ class AssetViewSet(BaseModelViewSet):
"""
model = Asset
- filterset_fields = ["folder", "parent_assets", "type", "risk_scenarios"]
+ filterset_fields = [
+ "folder",
+ "parent_assets",
+ "type",
+ "risk_scenarios",
+ "ebios_rm_studies",
+ ]
search_fields = ["name", "description", "business_value"]
@action(detail=False, name="Get type choices")
@@ -2025,13 +2031,22 @@ def post(self, request, *args, **kwargs):
return Response(status=status.HTTP_400_BAD_REQUEST)
+class QualificationViewSet(BaseModelViewSet):
+ """
+ API endpoint that allows qualifications to be viewed or edited.
+ """
+
+ model = Qualification
+ search_fields = ["name"]
+
+
class ComplianceAssessmentViewSet(BaseModelViewSet):
"""
API endpoint that allows compliance assessments to be viewed or edited.
"""
model = ComplianceAssessment
- filterset_fields = ["framework", "project", "status"]
+ filterset_fields = ["framework", "project", "status", "ebios_rm_studies"]
search_fields = ["name", "description", "ref_id"]
ordering_fields = ["name", "description"]
diff --git a/backend/ebios_rm/migrations/0001_initial.py b/backend/ebios_rm/migrations/0001_initial.py
index 66af48c5f..8980b23f0 100644
--- a/backend/ebios_rm/migrations/0001_initial.py
+++ b/backend/ebios_rm/migrations/0001_initial.py
@@ -163,6 +163,7 @@ class Migration(migrations.Migration):
related_name="ebios_rm_studies",
to="core.riskmatrix",
verbose_name="Risk matrix",
+ blank=True,
),
),
],
diff --git a/backend/ebios_rm/models.py b/backend/ebios_rm/models.py
index 99104508d..e0f39b2d4 100644
--- a/backend/ebios_rm/models.py
+++ b/backend/ebios_rm/models.py
@@ -31,6 +31,7 @@ class Status(models.TextChoices):
help_text=_(
"Risk matrix used as a reference for the study. Defaults to `urn:intuitem:risk:library:risk-matrix-4x4-ebios-rm`"
),
+ blank=True,
)
assets = models.ManyToManyField(
Asset,
@@ -297,6 +298,9 @@ class Meta:
verbose_name_plural = _("Stakeholders")
ordering = ["created_at"]
+ def __str__(self):
+ return f"{self.entity.name} - {self.category}"
+
def save(self, *args, **kwargs):
self.folder = self.ebios_rm_study.folder
super().save(*args, **kwargs)
diff --git a/backend/ebios_rm/serializers.py b/backend/ebios_rm/serializers.py
index e77c5c153..fb435c502 100644
--- a/backend/ebios_rm/serializers.py
+++ b/backend/ebios_rm/serializers.py
@@ -71,8 +71,8 @@ class Meta:
class FearedEventReadSerializer(BaseModelSerializer):
- str = serializers.CharField(source="__str__")
ebios_rm_study = FieldsRelatedField()
+ qualifications = FieldsRelatedField(["name"], many=True)
folder = FieldsRelatedField()
class Meta:
@@ -107,7 +107,7 @@ class Meta:
class StakeholderReadSerializer(BaseModelSerializer):
- str = serializers.CharField(source="__str__")
+ name = serializers.CharField(source="__str__")
ebios_rm_study = FieldsRelatedField()
folder = FieldsRelatedField()
entity = FieldsRelatedField()
diff --git a/backend/ebios_rm/views.py b/backend/ebios_rm/views.py
index ca0d048dd..1ca61f709 100644
--- a/backend/ebios_rm/views.py
+++ b/backend/ebios_rm/views.py
@@ -35,6 +35,10 @@ def status(self, request):
class FearedEventViewSet(BaseModelViewSet):
model = FearedEvent
+ filterset_fields = [
+ "ebios_rm_study",
+ ]
+
class RoToViewSet(BaseModelViewSet):
model = RoTo
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 9d5b87a36..36eb51bca 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -921,6 +921,22 @@
"ebiosWs5_4": "Assess and document residual risks",
"ebiosWs5_5": "Establish risk monitoring framework",
"activity": "Activity",
+ "ebiosRmMatrixHelpText": "Risk matrix used as a reference for the study. Defaults to `urn:intuitem:risk:library:risk-matrix-4x4-ebios-rm`",
+ "activityOne": "Activity 1",
+ "activityTwo": "Activity 2",
+ "ebiosRmStudy": "Ebios RM study",
+ "qualifications": "Qualifications",
+ "impacts": "Impacts",
+ "ebiosRmStudies": "Ebios RM studies",
"bringTheEvidences": "Bring the evidences",
- "bringTheEvidencesHelpText": "If disabled, the object will be duplicated without its evidences"
+ "bringTheEvidencesHelpText": "If disabled, the object will be duplicated without its evidences",
+ "currentDependency": "Current dependency",
+ "currentPenetration": "Current penetration",
+ "currentMaturity": "Current maturity",
+ "currentTrust": "Current trust",
+ "residualDependency": "Residual dependency",
+ "residualPenetration": "Residual penetration",
+ "residualMaturity": "Residual maturity",
+ "residualTrust": "Residual trust",
+ "selected": "Selected"
}
diff --git a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
index 7ceac4475..ff92026a7 100644
--- a/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
+++ b/frontend/src/lib/components/Breadcrumbs/Breadcrumbs.svelte
@@ -17,7 +17,7 @@
let tokenPath = '';
crumbs = tokens.map((t) => {
tokenPath += '/' + t;
- if (t === $breadcrumbObject.id) {
+ if (t === $breadcrumbObject?.id) {
if ($breadcrumbObject.name) {
t = $breadcrumbObject.name;
} else if ($breadcrumbObject.first_name && $breadcrumbObject.last_name) {
diff --git a/frontend/src/lib/components/Forms/ModelForm.svelte b/frontend/src/lib/components/Forms/ModelForm.svelte
index da9c227a7..e97998148 100644
--- a/frontend/src/lib/components/Forms/ModelForm.svelte
+++ b/frontend/src/lib/components/Forms/ModelForm.svelte
@@ -26,6 +26,9 @@
import SsoSettingsForm from './ModelForm/SsoSettingForm.svelte';
import FolderForm from './ModelForm/FolderForm.svelte';
import GeneralSettingsForm from './ModelForm/GeneralSettingForm.svelte';
+ import EbiosRmForm from './ModelForm/EbiosRmForm.svelte';
+ import FearedEventForm from './ModelForm/FearedEventForm.svelte';
+ import StakeholderForm from './ModelForm/StakeholderForm.svelte';
import AutocompleteSelect from './AutocompleteSelect.svelte';
@@ -255,6 +258,12 @@
{:else if URLModel === 'filtering-labels'}
+ {:else if URLModel === 'ebios-rm'}
+
+ {:else if URLModel === 'feared-events'}
+
+ {:else if URLModel === 'stakeholders'}
+
{/if}
{#if closeModal}
diff --git a/frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte b/frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
index c53338fa0..0bba0a55c 100644
--- a/frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
+++ b/frontend/src/lib/components/Forms/ModelForm/ComplianceAssessmentForm.svelte
@@ -38,6 +38,18 @@
options={getOptions({ objects: model.foreignKeys['baseline'] })}
/>
{/if}
+{#if initialData.ebios_rm_studies}
+
+{/if}
+ import type { SuperValidated } from 'sveltekit-superforms';
+ import type { ModelInfo, CacheLock } from '$lib/utils/types';
+ import TextField from '$lib/components/Forms/TextField.svelte';
+ import AutocompleteSelect from '$lib/components/Forms/AutocompleteSelect.svelte';
+ import Select from '$lib/components/Forms/Select.svelte';
+ import * as m from '$paraglide/messages.js';
+ import { getOptions } from '$lib/utils/crud';
+ import TextArea from '../TextArea.svelte';
+
+ export let form: SuperValidated;
+ export let model: ModelInfo;
+ export let cacheLocks: Record = {};
+ export let formDataCache: Record = {};
+ export let initialData: Record = {};
+ export let context: string;
+
+
+{#if context !== 'ebiosRmStudy'}
+
+
+
+
+{:else if context === 'ebiosRmStudy'}
+
+
{m.activityOne()}
+
+
+
+
+
+
+
+{/if}
diff --git a/frontend/src/lib/components/Forms/ModelForm/FearedEventForm.svelte b/frontend/src/lib/components/Forms/ModelForm/FearedEventForm.svelte
new file mode 100644
index 000000000..d3548da56
--- /dev/null
+++ b/frontend/src/lib/components/Forms/ModelForm/FearedEventForm.svelte
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/lib/components/Forms/ModelForm/StakeholderForm.svelte b/frontend/src/lib/components/Forms/ModelForm/StakeholderForm.svelte
new file mode 100644
index 000000000..561b7ea4d
--- /dev/null
+++ b/frontend/src/lib/components/Forms/ModelForm/StakeholderForm.svelte
@@ -0,0 +1,117 @@
+
+
+
+
+
+{#if context === 'edit'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{/if}
diff --git a/frontend/src/lib/utils/actions.ts b/frontend/src/lib/utils/actions.ts
index f85adeeed..c869da8fa 100644
--- a/frontend/src/lib/utils/actions.ts
+++ b/frontend/src/lib/utils/actions.ts
@@ -48,11 +48,16 @@ function getEndpoint({
urlModel: string;
event: RequestEvent;
}) {
+ const model = getModelInfo(urlModel);
if (action === 'create') {
- return `${BASE_API_URL}/${urlModel}/`;
+ return model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/`
+ : `${BASE_API_URL}/${urlModel}/`;
}
const id = event.params.id;
- return `${BASE_API_URL}/${urlModel}/${id}/`;
+ return model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${id}/`
+ : `${BASE_API_URL}/${urlModel}/${id}/`;
}
export async function handleErrorResponse({
@@ -199,9 +204,12 @@ export async function defaultDeleteFormAction({
const formData = await event.request.formData();
const schema = z.object({ id: z.string().uuid() });
const deleteForm = await superValidate(formData, zod(schema));
+ const model = getModelInfo(urlModel);
const id = deleteForm.data.id;
- const endpoint = `${BASE_API_URL}/${urlModel}/${id}/`;
+ const endpoint = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${id}/`
+ : `${BASE_API_URL}/${model.urlModel}/${id}/`;
if (!deleteForm.valid) {
console.error(deleteForm.errors);
diff --git a/frontend/src/lib/utils/crud.ts b/frontend/src/lib/utils/crud.ts
index 20573da0b..eec0b9923 100644
--- a/frontend/src/lib/utils/crud.ts
+++ b/frontend/src/lib/utils/crud.ts
@@ -131,6 +131,7 @@ export interface ModelMapEntry {
fileFields?: string[];
filters?: SelectField[];
path?: string;
+ endpointUrl?: string;
}
type ModelMap = {
@@ -460,7 +461,8 @@ export const URL_MODEL_MAP: ModelMap = {
{ field: 'framework', urlModel: 'frameworks' },
{ field: 'authors', urlModel: 'users' },
{ field: 'reviewers', urlModel: 'users', urlParams: 'is_third_party=false' },
- { field: 'baseline', urlModel: 'compliance-assessments' }
+ { field: 'baseline', urlModel: 'compliance-assessments' },
+ { field: 'ebios_rm_studies', urlModel: 'ebios-rm' }
],
selectFields: [{ field: 'status' }],
filters: [{ field: 'status' }]
@@ -584,6 +586,54 @@ export const URL_MODEL_MAP: ModelMap = {
{ field: 'entity', urlModel: 'entities' },
{ field: 'user', urlModel: 'users' }
]
+ },
+ qualifications: {
+ name: 'qualification',
+ localName: 'qualification',
+ localNamePlural: 'qualifications',
+ verboseName: 'Qualification',
+ verboseNamePlural: 'Qualifications'
+ },
+ 'ebios-rm': {
+ endpointUrl: 'ebios-rm/studies',
+ name: 'ebiosrmstudy',
+ localName: 'ebiosRMstudy',
+ localNamePlural: 'ebiosRMstudy',
+ verboseName: 'Ebios RMstudy',
+ verboseNamePlural: 'Ebios RMstudy',
+ foreignKeyFields: [
+ { field: 'risk_matrix', urlModel: 'risk-matrices' },
+ { field: 'assets', urlModel: 'assets' },
+ { field: 'authors', urlModel: 'users', urlParams: 'is_third_party=false' },
+ { field: 'reviewers', urlModel: 'users', urlParams: 'is_third_party=false' },
+ { field: 'folder', urlModel: 'folders', urlParams: 'content_type=DO' }
+ ]
+ },
+ 'feared-events': {
+ endpointUrl: 'ebios-rm/feared-events',
+ name: 'fearedevent',
+ localName: 'fearedEvent',
+ localNamePlural: 'fearedEvents',
+ verboseName: 'Feared event',
+ verboseNamePlural: 'Feared events',
+ foreignKeyFields: [
+ { field: 'ebios_rm_study', urlModel: 'ebios-rm' },
+ { field: 'assets', urlModel: 'assets' },
+ { field: 'qualifications', urlModel: 'qualifications' }
+ ]
+ },
+ stakeholders: {
+ endpointUrl: 'ebios-rm/stakeholders',
+ name: 'stakeholder',
+ localName: 'stakeholder',
+ localNamePlural: 'stakeholders',
+ verboseName: 'Stakeholder',
+ verboseNamePlural: 'Stakeholders',
+ foreignKeyFields: [
+ { field: 'entity', urlModel: 'entities' },
+ { field: 'applied_controls', urlModel: 'applied-controls' }
+ ],
+ selectFields: [{ field: 'category' }]
}
};
diff --git a/frontend/src/lib/utils/load.ts b/frontend/src/lib/utils/load.ts
index 08a762273..25155d91f 100644
--- a/frontend/src/lib/utils/load.ts
+++ b/frontend/src/lib/utils/load.ts
@@ -11,7 +11,9 @@ import { zod } from 'sveltekit-superforms/adapters';
import { z, type AnyZodObject } from 'zod';
export const loadDetail = async ({ event, model, id }) => {
- const endpoint = `${BASE_API_URL}/${model.urlModel}/${id}/`;
+ const endpoint = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${id}/`
+ : `${BASE_API_URL}/${model.urlModel}/${id}/`;
const res = await event.fetch(endpoint);
const data = await res.json();
diff --git a/frontend/src/lib/utils/schemas.ts b/frontend/src/lib/utils/schemas.ts
index bc96c4bb0..510cdc9e7 100644
--- a/frontend/src/lib/utils/schemas.ts
+++ b/frontend/src/lib/utils/schemas.ts
@@ -268,7 +268,8 @@ export const ComplianceAssessmentSchema = z.object({
reviewers: z.array(z.string().optional()).optional(),
baseline: z.string().optional().nullable(),
create_applied_controls_from_suggestions: z.boolean().optional().default(false),
- observation: z.string().optional().nullable()
+ observation: z.string().optional().nullable(),
+ ebios_rm_studies: z.string().uuid().optional().array().optional()
});
export const EvidenceSchema = z.object({
@@ -389,6 +390,46 @@ export const vulnerabilitySchema = z.object({
filtering_labels: z.string().optional().array().optional()
});
+export const ebiosRMSchema = z.object({
+ ...NameDescriptionMixin,
+ version: z.string().optional().default('0.1'),
+ ref_id: z.string().default(''),
+ risk_matrix: z.string().optional(),
+ authors: z.array(z.string().optional()).optional(),
+ reviewers: z.array(z.string().optional()).optional(),
+ observation: z.string().optional().nullable(),
+ assets: z.string().uuid().optional().array().optional(),
+ folder: z.string()
+});
+
+export const fearedEventsSchema = z.object({
+ ...NameDescriptionMixin,
+ ref_id: z.string().optional(),
+ gravity: z.number().optional(),
+ is_selected: z.boolean().optional(),
+ justification: z.string().optional(),
+ ebios_rm_study: z.string(),
+ assets: z.string().uuid().optional().array().optional(),
+ qualifications: z.string().optional().array().optional()
+});
+
+export const StakeholderSchema = z.object({
+ ebios_rm_study: z.string(),
+ applied_controls: z.string().uuid().optional().array().optional(),
+ category: z.string().optional(),
+ entity: z.string().optional(),
+ current_dependency: z.number().min(0).max(4).optional(),
+ current_penetration: z.number().min(0).max(4).optional(),
+ current_maturity: z.number().min(1).max(4).optional(),
+ current_trust: z.number().min(1).max(4).optional(),
+ residual_dependency: z.number().min(0).max(4).optional(),
+ residual_penetration: z.number().min(0).max(4).optional(),
+ residual_maturity: z.number().min(1).max(4).optional(),
+ residual_trust: z.number().min(1).max(4).optional(),
+ is_selected: z.boolean().optional(),
+ justification: z.string().optional()
+});
+
const SCHEMA_MAP: Record = {
folders: FolderSchema,
projects: ProjectSchema,
@@ -413,7 +454,10 @@ const SCHEMA_MAP: Record = {
representatives: representativeSchema,
solutions: solutionSchema,
vulnerabilities: vulnerabilitySchema,
- 'filtering-labels': FilteringLabelSchema
+ 'filtering-labels': FilteringLabelSchema,
+ 'ebios-rm': ebiosRMSchema,
+ 'feared-events': fearedEventsSchema,
+ stakeholders: StakeholderSchema
};
export const modelSchema = (model: string) => {
diff --git a/frontend/src/lib/utils/table.ts b/frontend/src/lib/utils/table.ts
index 012b8268c..b52866860 100644
--- a/frontend/src/lib/utils/table.ts
+++ b/frontend/src/lib/utils/table.ts
@@ -527,5 +527,17 @@ export const listViewFields: ListViewFieldsConfig = {
representatives: {
head: ['email', 'entity', 'role'],
body: ['email', 'entity', 'role']
+ },
+ 'ebios-rm': {
+ head: ['name', 'description'],
+ body: ['name', 'description']
+ },
+ 'feared-events': {
+ head: ['selected', 'assets', 'fearedEvent', 'qualifications', 'gravity'],
+ body: ['is_selected', 'assets', 'description', 'qualifications', 'gravity']
+ },
+ stakeholders: {
+ head: ['entity', 'category', 'current_criticality', 'applied_controls', 'residual_criticality'],
+ body: ['entity', 'category', 'current_criticality', 'applied_controls', 'residual_criticality']
}
};
diff --git a/frontend/src/lib/utils/types.ts b/frontend/src/lib/utils/types.ts
index edff27b87..0ba2c2873 100644
--- a/frontend/src/lib/utils/types.ts
+++ b/frontend/src/lib/utils/types.ts
@@ -51,7 +51,10 @@ export const URL_MODEL = [
'solutions',
'representatives',
'vulnerabilities',
- 'filtering-labels'
+ 'filtering-labels',
+ 'feared-events',
+ 'stakeholders'
+ // 'ebios-rm',
] as const;
export const THIRD_PARTY_URL_MODEL = ['compliance-assessments', 'evidences'] as const;
diff --git a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts
index 128452015..45ce69a1f 100644
--- a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts
+++ b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/+layout.server.ts
@@ -14,7 +14,9 @@ export const load = (async ({ fetch, params }) => {
const fetch_function = CUSTOM_MODEL_FETCH_MAP[params.model];
data = await fetch_function({ fetch, params }, languageTag());
} else {
- const endpoint = `${BASE_API_URL}/${params.model}/${model.listViewUrlParams || ''}`;
+ const endpoint = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${model.listViewUrlParams || ''}`
+ : `${BASE_API_URL}/${params.model}/${model.listViewUrlParams || ''}`;
const res = await fetch(endpoint);
data = await res.json().then((res) => res.results);
}
diff --git a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/edit/+layout.server.ts b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/edit/+layout.server.ts
index 8639fad8d..f7e5c4760 100644
--- a/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/edit/+layout.server.ts
+++ b/frontend/src/routes/(app)/(internal)/[model=urlmodel]/[id=uuid]/edit/+layout.server.ts
@@ -12,11 +12,13 @@ import { zod } from 'sveltekit-superforms/adapters';
export const load: LayoutServerLoad = async (event) => {
const URLModel = event.params.model!;
const schema = modelSchema(event.params.model);
- const objectEndpoint = `${BASE_API_URL}/${event.params.model}/${event.params.id}/object/`;
+ const model = getModelInfo(event.params.model!);
+ const objectEndpoint = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${event.params.id}/object/`
+ : `${BASE_API_URL}/${event.params.model}/${event.params.id}/object/`;
const object = await event.fetch(objectEndpoint).then((res) => res.json());
const form = await superValidate(object, zod(schema), { errors: false });
- const model = getModelInfo(event.params.model!);
const foreignKeyFields = model.foreignKeyFields;
const selectFields = model.selectFields;
@@ -46,7 +48,10 @@ export const load: LayoutServerLoad = async (event) => {
if (foreignKeyFields) {
for (const keyField of foreignKeyFields) {
const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
- const url = `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
+ const keyModel = getModelInfo(keyField.urlModel);
+ const url = keyModel.endpointUrl
+ ? `${BASE_API_URL}/${keyModel.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${keyModel.urlModel}/${queryParams}`;
const response = await event.fetch(url);
if (response.ok) {
foreignKeys[keyField.field] = await response.json().then((data) => data.results);
@@ -60,7 +65,7 @@ export const load: LayoutServerLoad = async (event) => {
if (selectFields) {
for (const selectField of selectFields) {
- const url = `${BASE_API_URL}/${event.params.model}/${
+ const url = `${BASE_API_URL}/${model.endpointUrl ?? event.params.model}/${
selectField.detail ? event.params.id + '/' : ''
}${selectField.field}/`;
const response = await event.fetch(url);
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/+page.server.ts
new file mode 100644
index 000000000..1c395758c
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/+page.server.ts
@@ -0,0 +1,99 @@
+import { defaultDeleteFormAction, defaultWriteFormAction } from '$lib/utils/actions';
+import { BASE_API_URL } from '$lib/utils/constants';
+import {
+ getModelInfo,
+ urlParamModelForeignKeyFields,
+ urlParamModelSelectFields
+} from '$lib/utils/crud';
+import { modelSchema } from '$lib/utils/schemas';
+import type { ModelInfo, urlModel } from '$lib/utils/types';
+import { type Actions } from '@sveltejs/kit';
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import { z } from 'zod';
+import type { PageServerLoad } from './$types';
+import { listViewFields } from '$lib/utils/table';
+import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton';
+
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const schema = z.object({ id: z.string().uuid() });
+ const deleteForm = await superValidate(zod(schema));
+ const URLModel = 'ebios-rm';
+ const createSchema = modelSchema(URLModel);
+ const createForm = await superValidate(zod(createSchema));
+ const model: ModelInfo = getModelInfo(URLModel);
+ const foreignKeyFields = urlParamModelForeignKeyFields(URLModel);
+ const selectFields = urlParamModelSelectFields(URLModel);
+
+ const foreignKeys: Record = {};
+
+ for (const keyField of foreignKeyFields) {
+ const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
+ const url = `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
+ const response = await fetch(url);
+ if (response.ok) {
+ foreignKeys[keyField.field] = await response.json().then((data) => data.results);
+ } else {
+ console.error(`Failed to fetch data for ${keyField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['foreignKeys'] = foreignKeys;
+
+ const selectOptions: Record = {};
+
+ for (const selectField of selectFields) {
+ if (selectField.detail) continue;
+ const url = `${BASE_API_URL}/${URLModel}/${selectField.field}/`;
+ const response = await fetch(url);
+ if (response.ok) {
+ selectOptions[selectField.field] = await response.json().then((data) =>
+ Object.entries(data).map(([key, value]) => ({
+ label: value,
+ value: key
+ }))
+ );
+ } else {
+ console.error(`Failed to fetch data for ${selectField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['selectOptions'] = selectOptions;
+
+ const endpoint = `${BASE_API_URL}/${model.endpointUrl}`;
+ const res = await fetch(endpoint);
+ const data = await res.json().then((res) => res.results);
+
+ const bodyData = tableSourceMapper(data, listViewFields[URLModel as urlModel].body);
+
+ const headData: Record = listViewFields[URLModel as urlModel].body.reduce(
+ (obj, key, index) => {
+ obj[key] = listViewFields[URLModel as urlModel].head[index];
+ return obj;
+ },
+ {}
+ );
+
+ const table: TableSource = {
+ head: headData,
+ body: bodyData,
+ meta: data // metaData
+ };
+
+ return { createForm, deleteForm, model, URLModel, table };
+};
+
+export const actions: Actions = {
+ create: async (event) => {
+ // const redirectToWrittenObject = Boolean(event.params.model === 'entity-assessments');
+ return defaultWriteFormAction({
+ event,
+ urlModel: 'ebios-rm',
+ action: 'create'
+ // redirectToWrittenObject: redirectToWrittenObject
+ });
+ },
+ delete: async (event) => {
+ return defaultDeleteFormAction({ event, urlModel: 'ebios-rm' });
+ }
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/+page.svelte
new file mode 100644
index 000000000..4d1ae36d4
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/+page.svelte
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.server.ts
new file mode 100644
index 000000000..cb361f306
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.server.ts
@@ -0,0 +1,7 @@
+import { loadDetail } from '$lib/utils/load';
+import type { PageServerLoad } from './$types';
+import { getModelInfo } from '$lib/utils/crud';
+
+export const load: PageServerLoad = async (event) => {
+ return await loadDetail({ event, model: getModelInfo('ebios-rm'), id: event.params.id });
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte
index 33000896f..17fbf4e1c 100644
--- a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/+page.svelte
@@ -2,12 +2,36 @@
import * as m from '$paraglide/messages';
import { safeTranslate } from '$lib/utils/i18n';
import Tile from './Tile.svelte';
- const data = {
+ import { page } from '$app/stores';
+ import type { PageData } from './$types';
+ import { breadcrumbObject } from '$lib/utils/stores';
+
+ export let data: PageData;
+
+ $: breadcrumbObject.set(data.data);
+
+ const dummydata = {
ws1: [
- { title: safeTranslate(m.ebiosWs1_1()), status: 'done', href: '#' },
- { title: safeTranslate(m.ebiosWs1_2()), status: 'done', href: '#' },
- { title: safeTranslate(m.ebiosWs1_3()), status: 'to_do', href: '#' },
- { title: safeTranslate(m.ebiosWs1_4()), status: 'to_do', href: '#' }
+ {
+ title: safeTranslate(m.ebiosWs1_1()),
+ status: 'done',
+ href: `${$page.url.pathname}/workshop-one/ebios-rm-study?next=${$page.url.pathname}`
+ },
+ {
+ title: safeTranslate(m.ebiosWs1_2()),
+ status: 'done',
+ href: `${$page.url.pathname}/workshop-one/ebios-rm-study?next=${$page.url.pathname}`
+ },
+ {
+ title: safeTranslate(m.ebiosWs1_3()),
+ status: 'to_do',
+ href: `${$page.url.pathname}/workshop-one/feared-events?next=${$page.url.pathname}`
+ },
+ {
+ title: safeTranslate(m.ebiosWs1_4()),
+ status: 'to_do',
+ href: `${$page.url.pathname}/workshop-one/base?next=${$page.url.pathname}`
+ }
],
ws2: [
{ title: safeTranslate(m.ebiosWs2_1()), status: 'to_do', href: '#' },
@@ -15,9 +39,17 @@
{ title: safeTranslate(m.ebiosWs2_3()), status: 'to_do', href: '#' }
],
ws3: [
- { title: safeTranslate(m.ebiosWs3_1()), status: 'to_do', href: '#' },
+ {
+ title: safeTranslate(m.ebiosWs3_1()),
+ status: 'to_do',
+ href: `${$page.url.pathname}/workshop-three/ecosystem?next=${$page.url.pathname}`
+ },
{ title: safeTranslate(m.ebiosWs3_2()), status: 'to_do', href: '#' },
- { title: safeTranslate(m.ebiosWs3_3()), status: 'done', href: '#' }
+ {
+ title: safeTranslate(m.ebiosWs3_3()),
+ status: 'done',
+ href: `${$page.url.pathname}/workshop-three/ecosystem?next=${$page.url.pathname}`
+ }
],
ws4: [
{ title: safeTranslate(m.ebiosWs4_1()), status: 'to_do', href: '#' },
@@ -33,15 +65,15 @@
};
-
+
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/base/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/base/+page.server.ts
new file mode 100644
index 000000000..b4dd9b6c2
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/base/+page.server.ts
@@ -0,0 +1,110 @@
+import { defaultDeleteFormAction, defaultWriteFormAction } from '$lib/utils/actions';
+import { BASE_API_URL } from '$lib/utils/constants';
+import {
+ getModelInfo,
+ urlParamModelForeignKeyFields,
+ urlParamModelSelectFields
+} from '$lib/utils/crud';
+import { modelSchema } from '$lib/utils/schemas';
+import type { ModelInfo, urlModel } from '$lib/utils/types';
+import { type Actions } from '@sveltejs/kit';
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import { z } from 'zod';
+import type { PageServerLoad } from './$types';
+import { listViewFields } from '$lib/utils/table';
+import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton';
+
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const schema = z.object({ id: z.string().uuid() });
+ const deleteForm = await superValidate(zod(schema));
+ const URLModel = 'compliance-assessments';
+ const createSchema = modelSchema(URLModel);
+ const initialData = {
+ ebios_rm_studies: [params.id]
+ };
+ const createForm = await superValidate(initialData, zod(createSchema), { errors: false });
+ const model: ModelInfo = getModelInfo(URLModel);
+ const foreignKeyFields = urlParamModelForeignKeyFields(URLModel);
+ const selectFields = model.selectFields;
+
+ const foreignKeys: Record
= {};
+
+ for (const keyField of foreignKeyFields) {
+ const model = getModelInfo(keyField.urlModel);
+ const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
+ const url = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${model.urlModel}/${queryParams}`;
+ const response = await fetch(url);
+ if (response.ok) {
+ foreignKeys[keyField.field] = await response.json().then((data) => data.results);
+ } else {
+ console.error(`Failed to fetch data for ${keyField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['foreignKeys'] = foreignKeys;
+
+ const selectOptions: Record = {};
+
+ if (selectFields) {
+ for (const selectField of selectFields) {
+ const url = `${BASE_API_URL}/${URLModel}/${
+ selectField.detail ? params.id + '/' : ''
+ }${selectField.field}/`;
+ const response = await fetch(url);
+ if (response.ok) {
+ selectOptions[selectField.field] = await response.json().then((data) =>
+ Object.entries(data).map(([key, value]) => ({
+ label: value,
+ value: key
+ }))
+ );
+ } else {
+ console.error(`Failed to fetch data for ${selectField.field}: ${response.statusText}`);
+ }
+ }
+ }
+
+ model['selectOptions'] = selectOptions;
+
+ const endpoint = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}?ebios_rm_studies=${params.id}`
+ : `${BASE_API_URL}/${model.urlModel}?ebios_rm_studies=${params.id}`;
+ const res = await fetch(endpoint);
+ const data = await res.json().then((res) => res.results);
+
+ const bodyData = tableSourceMapper(data, listViewFields[URLModel as urlModel].body);
+
+ const headData: Record = listViewFields[URLModel as urlModel].body.reduce(
+ (obj, key, index) => {
+ obj[key] = listViewFields[URLModel as urlModel].head[index];
+ return obj;
+ },
+ {}
+ );
+
+ const table: TableSource = {
+ head: headData,
+ body: bodyData,
+ meta: data // metaData
+ };
+
+ return { createForm, deleteForm, model, URLModel, table };
+};
+
+export const actions: Actions = {
+ create: async (event) => {
+ // const redirectToWrittenObject = Boolean(event.params.model === 'entity-assessments');
+ return defaultWriteFormAction({
+ event,
+ urlModel: 'compliance-assessments',
+ action: 'create'
+ // redirectToWrittenObject: redirectToWrittenObject
+ });
+ },
+ delete: async (event) => {
+ return defaultDeleteFormAction({ event, urlModel: 'compliance-assessments' });
+ }
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/base/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/base/+page.svelte
new file mode 100644
index 000000000..08ebedae8
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/base/+page.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/ebios-rm-study/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/ebios-rm-study/+page.server.ts
new file mode 100644
index 000000000..e28cc52ea
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/ebios-rm-study/+page.server.ts
@@ -0,0 +1,65 @@
+import { BASE_API_URL } from '$lib/utils/constants';
+import { getModelInfo } from '$lib/utils/crud';
+import { modelSchema } from '$lib/utils/schemas';
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import type { PageServerLoad, Actions } from './$types';
+import { defaultWriteFormAction } from '$lib/utils/actions';
+
+export const load: PageServerLoad = async (event) => {
+ const URLModel = 'ebios-rm';
+ const model = getModelInfo(URLModel);
+ const schema = modelSchema(URLModel);
+ const objectEndpoint = `${BASE_API_URL}/${model.endpointUrl}/${event.params.id}/object/`;
+ const objectResponse = await event.fetch(objectEndpoint);
+ const object = await objectResponse.json();
+
+ const form = await superValidate(object, zod(schema), { errors: false });
+ const foreignKeyFields = model.foreignKeyFields;
+ const selectFields = model.selectFields;
+
+ const foreignKeys: Record = {};
+
+ if (foreignKeyFields) {
+ for (const keyField of foreignKeyFields) {
+ const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
+ const url = `${BASE_API_URL}/${keyField.urlModel}/${queryParams}`;
+ const response = await event.fetch(url);
+ if (response.ok) {
+ foreignKeys[keyField.field] = await response.json().then((data) => data.results);
+ } else {
+ console.error(`Failed to fetch data for ${keyField.field}: ${response.statusText}`);
+ }
+ }
+ }
+
+ const selectOptions: Record = {};
+
+ if (selectFields) {
+ for (const selectField of selectFields) {
+ const url = `${BASE_API_URL}/${URLModel}/${
+ selectField.detail ? event.params.id + '/' : ''
+ }${selectField.field}/`;
+ const response = await event.fetch(url);
+ if (response.ok) {
+ selectOptions[selectField.field] = await response.json().then((data) =>
+ Object.entries(data).map(([key, value]) => ({
+ label: value,
+ value: key
+ }))
+ );
+ } else {
+ console.error(`Failed to fetch data for ${selectField.field}: ${response.statusText}`);
+ }
+ }
+ }
+ model.foreignKeys = foreignKeys;
+ model.selectOptions = selectOptions;
+ return { form, model, object, foreignKeys, selectOptions, URLModel };
+};
+
+export const actions: Actions = {
+ default: async (event) => {
+ return defaultWriteFormAction({ event, urlModel: 'ebios-rm', action: 'edit' });
+ }
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/ebios-rm-study/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/ebios-rm-study/+page.svelte
new file mode 100644
index 000000000..beafb4be4
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/ebios-rm-study/+page.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/feared-events/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/feared-events/+page.server.ts
new file mode 100644
index 000000000..bb8c37006
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/feared-events/+page.server.ts
@@ -0,0 +1,84 @@
+import { defaultDeleteFormAction, defaultWriteFormAction } from '$lib/utils/actions';
+import { BASE_API_URL } from '$lib/utils/constants';
+import {
+ getModelInfo,
+ urlParamModelForeignKeyFields,
+ urlParamModelSelectFields
+} from '$lib/utils/crud';
+import { modelSchema } from '$lib/utils/schemas';
+import type { ModelInfo, urlModel } from '$lib/utils/types';
+import { type Actions } from '@sveltejs/kit';
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import { z } from 'zod';
+import type { PageServerLoad } from './$types';
+import { listViewFields } from '$lib/utils/table';
+import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton';
+
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const schema = z.object({ id: z.string().uuid() });
+ const deleteForm = await superValidate(zod(schema));
+ const URLModel = 'feared-events';
+ const createSchema = modelSchema(URLModel);
+ const initialData = {
+ ebios_rm_study: params.id
+ };
+ const createForm = await superValidate(initialData, zod(createSchema), { errors: false });
+ const model: ModelInfo = getModelInfo(URLModel);
+ const foreignKeyFields = urlParamModelForeignKeyFields(URLModel);
+
+ const foreignKeys: Record = {};
+
+ for (const keyField of foreignKeyFields) {
+ const model = getModelInfo(keyField.urlModel);
+ const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
+ const url = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${model.urlModel}/${queryParams}`;
+ const response = await fetch(url);
+ if (response.ok) {
+ foreignKeys[keyField.field] = await response.json().then((data) => data.results);
+ } else {
+ console.error(`Failed to fetch data for ${keyField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['foreignKeys'] = foreignKeys;
+
+ const endpoint = `${BASE_API_URL}/${model.endpointUrl}?ebios_rm_study=${params.id}`;
+ const res = await fetch(endpoint);
+ const data = await res.json().then((res) => res.results);
+
+ const bodyData = tableSourceMapper(data, listViewFields[URLModel as urlModel].body);
+
+ const headData: Record = listViewFields[URLModel as urlModel].body.reduce(
+ (obj, key, index) => {
+ obj[key] = listViewFields[URLModel as urlModel].head[index];
+ return obj;
+ },
+ {}
+ );
+
+ const table: TableSource = {
+ head: headData,
+ body: bodyData,
+ meta: data // metaData
+ };
+
+ return { createForm, deleteForm, model, URLModel, table };
+};
+
+export const actions: Actions = {
+ create: async (event) => {
+ // const redirectToWrittenObject = Boolean(event.params.model === 'entity-assessments');
+ return defaultWriteFormAction({
+ event,
+ urlModel: 'feared-events',
+ action: 'create'
+ // redirectToWrittenObject: redirectToWrittenObject
+ });
+ },
+ delete: async (event) => {
+ return defaultDeleteFormAction({ event, urlModel: 'feared-events' });
+ }
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/feared-events/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/feared-events/+page.svelte
new file mode 100644
index 000000000..08ebedae8
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-one/feared-events/+page.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-three/ecosystem/+page.server.ts b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-three/ecosystem/+page.server.ts
new file mode 100644
index 000000000..3449f6aa3
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-three/ecosystem/+page.server.ts
@@ -0,0 +1,104 @@
+import { defaultDeleteFormAction, defaultWriteFormAction } from '$lib/utils/actions';
+import { BASE_API_URL } from '$lib/utils/constants';
+import { getModelInfo, urlParamModelForeignKeyFields } from '$lib/utils/crud';
+import { modelSchema } from '$lib/utils/schemas';
+import type { ModelInfo, urlModel } from '$lib/utils/types';
+import { type Actions } from '@sveltejs/kit';
+import { superValidate } from 'sveltekit-superforms';
+import { zod } from 'sveltekit-superforms/adapters';
+import { z } from 'zod';
+import type { PageServerLoad } from './$types';
+import { listViewFields } from '$lib/utils/table';
+import { tableSourceMapper, type TableSource } from '@skeletonlabs/skeleton';
+
+export const load: PageServerLoad = async ({ params, fetch }) => {
+ const schema = z.object({ id: z.string().uuid() });
+ const deleteForm = await superValidate(zod(schema));
+ const URLModel = 'stakeholders';
+ const createSchema = modelSchema(URLModel);
+ const initialData = {
+ ebios_rm_study: params.id
+ };
+ const createForm = await superValidate(initialData, zod(createSchema), { errors: false });
+ const model: ModelInfo = getModelInfo(URLModel);
+ const foreignKeyFields = urlParamModelForeignKeyFields(URLModel);
+
+ const selectOptions: Record = {};
+ if (model.selectFields) {
+ await Promise.all(
+ model.selectFields.map(async (selectField) => {
+ const url = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${selectField.field}`
+ : `${BASE_API_URL}/${model.urlModel}/${selectField.field}`;
+ const response = await fetch(url);
+ if (!response.ok) {
+ console.error(`Failed to fetch data from ${url}: ${response.statusText}`);
+ return null;
+ }
+ const data = await response.json();
+ if (data) {
+ selectOptions[selectField.field] = Object.entries(data).map(([key, value]) => ({
+ label: value,
+ value: key
+ }));
+ }
+ })
+ );
+ }
+ model.selectOptions = selectOptions;
+
+ const foreignKeys: Record = {};
+
+ for (const keyField of foreignKeyFields) {
+ const model = getModelInfo(keyField.urlModel);
+ const queryParams = keyField.urlParams ? `?${keyField.urlParams}` : '';
+ const url = model.endpointUrl
+ ? `${BASE_API_URL}/${model.endpointUrl}/${queryParams}`
+ : `${BASE_API_URL}/${model.urlModel}/${queryParams}`;
+ const response = await fetch(url);
+ if (response.ok) {
+ foreignKeys[keyField.field] = await response.json().then((data) => data.results);
+ } else {
+ console.error(`Failed to fetch data for ${keyField.field}: ${response.statusText}`);
+ }
+ }
+
+ model['foreignKeys'] = foreignKeys;
+
+ const endpoint = `${BASE_API_URL}/${model.endpointUrl}?ebios_rm_study=${params.id}`;
+ const res = await fetch(endpoint);
+ const data = await res.json().then((res) => res.results);
+
+ const bodyData = tableSourceMapper(data, listViewFields[URLModel as urlModel].body);
+
+ const headData: Record = listViewFields[URLModel as urlModel].body.reduce(
+ (obj, key, index) => {
+ obj[key] = listViewFields[URLModel as urlModel].head[index];
+ return obj;
+ },
+ {}
+ );
+
+ const table: TableSource = {
+ head: headData,
+ body: bodyData,
+ meta: data // metaData
+ };
+
+ return { createForm, deleteForm, model, URLModel, table };
+};
+
+export const actions: Actions = {
+ create: async (event) => {
+ // const redirectToWrittenObject = Boolean(event.params.model === 'entity-assessments');
+ return defaultWriteFormAction({
+ event,
+ urlModel: 'stakeholders',
+ action: 'create'
+ // redirectToWrittenObject: redirectToWrittenObject
+ });
+ },
+ delete: async (event) => {
+ return defaultDeleteFormAction({ event, urlModel: 'stakeholders' });
+ }
+};
diff --git a/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-three/ecosystem/+page.svelte b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-three/ecosystem/+page.svelte
new file mode 100644
index 000000000..08ebedae8
--- /dev/null
+++ b/frontend/src/routes/(app)/(internal)/ebios-rm/[id=uuid]/workshop-three/ecosystem/+page.svelte
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/routes/(app)/(internal)/experimental/+page.svelte b/frontend/src/routes/(app)/(internal)/experimental/+page.svelte
index d9a1f5d98..7964bcdf6 100644
--- a/frontend/src/routes/(app)/(internal)/experimental/+page.svelte
+++ b/frontend/src/routes/(app)/(internal)/experimental/+page.svelte
@@ -27,10 +27,4 @@
link="assets/graph"
tags={['analysis', 'assets']}
/>
-