Skip to content

Commit

Permalink
Merge branch 'development' into feat/create-text-editor-field
Browse files Browse the repository at this point in the history
  • Loading branch information
deeonwuli authored Jan 24, 2025
2 parents c63bfa2 + 37f671d commit 945ebbf
Show file tree
Hide file tree
Showing 38 changed files with 1,440 additions and 14 deletions.
30 changes: 28 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2025-01-17T09:18:30.045Z\n"
"PO-Revision-Date: 2025-01-17T09:18:30.045Z\n"
"POT-Creation-Date: 2025-01-20T08:19:05.880Z\n"
"PO-Revision-Date: 2025-01-20T08:19:05.880Z\n"

msgid "Low"
msgstr ""
Expand Down Expand Up @@ -150,6 +150,9 @@ msgstr ""
msgid "Last updated: "
msgstr ""

msgid "Add"
msgstr ""

msgid "Median"
msgstr ""

Expand Down Expand Up @@ -338,6 +341,14 @@ msgid ""
"you sure you want to continue?"
msgstr ""

msgid "Resource saved successfully"
msgstr ""

msgid ""
"You have uploaded a new resource with an existing file name. This action "
"will replace the current resource. Are you sure you want to continue?"
msgstr ""

msgid "Add another"
msgstr ""

Expand Down Expand Up @@ -404,9 +415,24 @@ msgstr ""
msgid "Are you sure you want to delete this team role?"
msgstr ""

msgid "Are you sure you want to delete this resource? This action cannot be undone."
msgstr ""

msgid "Upload File"
msgstr ""

msgid "Resources"
msgstr ""

msgid "No resources created"
msgstr ""

msgid "You can upload a file to create a new resource"
msgstr ""

msgid "Contact admin to create resources"
msgstr ""

msgctxt "DATA"
msgid "HISTORICAL_CASE"
msgstr ""
32 changes: 28 additions & 4 deletions i18n/es.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"POT-Creation-Date: 2025-01-14T15:51:01.421Z\n"
"POT-Creation-Date: 2025-01-15T16:05:28.418Z\n"
"PO-Revision-Date: 2018-10-25T09:02:35.143Z\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
Expand Down Expand Up @@ -149,6 +149,9 @@ msgstr ""
msgid "Last updated: "
msgstr ""

msgid "Add"
msgstr "Añadir"

msgid "Median"
msgstr ""

Expand Down Expand Up @@ -337,6 +340,14 @@ msgid ""
"you sure you want to continue?"
msgstr ""

msgid "Resource saved successfully"
msgstr ""

msgid ""
"You have uploaded a new resource with an existing file name. This action "
"will replace the current resource. Are you sure you want to continue?"
msgstr ""

msgid "Add another"
msgstr ""

Expand Down Expand Up @@ -403,16 +414,29 @@ msgstr ""
msgid "Are you sure you want to delete this team role?"
msgstr ""

msgid ""
"Are you sure you want to delete this resource? This action cannot be undone."
msgstr ""

msgid "Upload File"
msgstr ""

msgid "Resources"
msgstr ""

msgid "No resources created"
msgstr ""

msgid "You can upload a file to create a new resource"
msgstr ""

msgid "Contact admin to create resources"
msgstr ""

msgctxt "DATA"
msgid "HISTORICAL_CASE"
msgstr ""

#~ msgid "Add"
#~ msgstr "Añadir"

#~ msgid "List"
#~ msgstr "Listar"

Expand Down
24 changes: 24 additions & 0 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,16 @@ import { UserGroupD2Repository } from "./data/repositories/UserGroupD2Repository
import { UserGroupRepository } from "./domain/repositories/UserGroupRepository";
import { UserGroupTestRepository } from "./data/repositories/test/UserGroupTestRepository";
import { GetAllAlertsPerformanceOverviewMetricsUseCase } from "./domain/usecases/GetAllAlertsPerformanceOverviewMetricsUseCase";
import { ResourceRepository } from "./domain/repositories/ResourceRepository";
import { ResourceD2Repository } from "./data/repositories/ResourceD2Repository";
import { ResourceTestRepository } from "./data/repositories/test/ResourceTestRepository";
import { GetResourcesUseCase } from "./domain/usecases/GetResourcesUseCase";
import { DownloadResourceFileUseCase } from "./domain/usecases/DownloadResourceFileUseCase";
import { DeleteResourceFileUseCase } from "./domain/usecases/DeleteResourceFileUseCase";
import { ResourceFileTestRepository } from "./data/repositories/test/ResourceFileTestRepository";
import { ResourceFileRepository } from "./domain/repositories/ResourceFileRepository";
import { ResourceFileD2Repository } from "./data/repositories/ResourceFileD2Repository";
import { GetResourceUserPermissionsUseCase } from "./domain/usecases/GetResourceUserPermissionsUseCase";

export type CompositionRoot = ReturnType<typeof getCompositionRoot>;

Expand All @@ -95,6 +105,8 @@ type Repositories = {
configurationsRepository: ConfigurationsRepository;
casesFileRepository: CasesFileRepository;
userGroupRepository: UserGroupRepository;
resourceRepository: ResourceRepository;
resourceFileRepository: ResourceFileRepository;
};

function getCompositionRoot(repositories: Repositories) {
Expand Down Expand Up @@ -150,6 +162,14 @@ function getCompositionRoot(repositories: Repositories) {
charts: {
getCases: new GetChartConfigByTypeUseCase(repositories.chartConfigRepository),
},
resources: {
get: new GetResourcesUseCase(repositories.resourceRepository),
downloadResourceFile: new DownloadResourceFileUseCase(
repositories.resourceFileRepository
),
deleteResourceFile: new DeleteResourceFileUseCase(repositories),
getPermissions: new GetResourceUserPermissionsUseCase(repositories),
},
};
}

Expand All @@ -173,6 +193,8 @@ export function getWebappCompositionRoot(api: D2Api) {
configurationsRepository: new ConfigurationsD2Repository(api),
casesFileRepository: new CasesFileD2Repository(api, dataStoreClient),
userGroupRepository: new UserGroupD2Repository(api),
resourceRepository: new ResourceD2Repository(api),
resourceFileRepository: new ResourceFileD2Repository(api),
};

return getCompositionRoot(repositories);
Expand All @@ -197,6 +219,8 @@ export function getTestCompositionRoot() {
configurationsRepository: new ConfigurationsTestRepository(),
casesFileRepository: new CasesFileTestRepository(),
userGroupRepository: new UserGroupTestRepository(),
resourceRepository: new ResourceTestRepository(),
resourceFileRepository: new ResourceFileTestRepository(),
};

return getCompositionRoot(repositories);
Expand Down
54 changes: 54 additions & 0 deletions src/data/repositories/ResourceD2Repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { D2Api } from "../../types/d2-api";
import { ResourceRepository } from "../../domain/repositories/ResourceRepository";
import { DataStoreClient } from "../DataStoreClient";
import { isExistingResource, Resource } from "../../domain/entities/resources/Resource";
import { FutureData } from "../api-futures";
import { Future } from "../../domain/entities/generic/Future";
import { Id } from "../../domain/entities/Ref";

const RESOURCES_KEY = "resources";

export class ResourceD2Repository implements ResourceRepository {
private dataStoreClient: DataStoreClient;

constructor(private api: D2Api) {
this.dataStoreClient = new DataStoreClient(api);
}

getAllResources(): FutureData<Resource[]> {
return this.dataStoreClient
.getObject<Resource[]>(RESOURCES_KEY)
.flatMap(resources => Future.success(resources ?? []));
}

saveResource(resource: Resource): FutureData<void> {
if (!resource) return Future.error(new Error("No resource form data found"));

return this.getAllResources().flatMap(resourcesInDataStore => {
const updatedResources = this.getResourcesToSave(resourcesInDataStore, resource);

return this.dataStoreClient.saveObject<Resource[]>(RESOURCES_KEY, updatedResources);
});
}

private getResourcesToSave(resourcesInDataStore: Resource[], resource: Resource) {
const isResourceExisting = isExistingResource(resourcesInDataStore, resource);

const updatedResources = isResourceExisting
? resourcesInDataStore.map(resourceInDataStore =>
isResourceExisting ? resource : resourceInDataStore
)
: [...resourcesInDataStore, resource];
return updatedResources;
}

deleteResource(fileId: Id): FutureData<void> {
return this.getAllResources().flatMap(resources => {
const updatedResources = resources.filter(
resource => resource.resourceFileId !== fileId
);

return this.dataStoreClient.saveObject<Resource[]>(RESOURCES_KEY, updatedResources);
});
}
}
41 changes: 41 additions & 0 deletions src/data/repositories/ResourceFileD2Repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { D2Api } from "../../types/d2-api";
import { ResourceFile } from "../../domain/entities/resources/ResourceFile";
import { apiToFuture, FutureData } from "../api-futures";
import { Future } from "../../domain/entities/generic/Future";
import { Id } from "../../domain/entities/Ref";
import { ResourceFileRepository } from "../../domain/repositories/ResourceFileRepository";

export class ResourceFileD2Repository implements ResourceFileRepository {
constructor(private api: D2Api) {}

uploadFile(file: File): FutureData<Id> {
return apiToFuture(
this.api.files.upload({
name: file.name,
data: file,
})
).flatMap(fileResource => Future.success(fileResource.id));
}

downloadFile(fileId: Id): FutureData<ResourceFile> {
if (!fileId) return Future.error(new Error("No file id found"));

return apiToFuture(this.api.files.get(fileId))
.map(blob => {
return new File([blob], "file", { type: blob.type });
})
.flatMap(file =>
Future.success({
fileId: fileId,
file: file,
})
);
}

deleteResourceFile(fileId: Id): FutureData<void> {
return apiToFuture(this.api.files.delete(fileId)).flatMap(response => {
if (response.httpStatus === "OK") return Future.success(undefined);
else return Future.error(new Error("Error while deleting resource file"));
});
}
}
22 changes: 22 additions & 0 deletions src/data/repositories/test/ResourceFileTestRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Id } from "@eyeseetea/d2-api";
import { Future } from "../../../domain/entities/generic/Future";
import { FutureData } from "../../api-futures";
import { ResourceFileRepository } from "../../../domain/repositories/ResourceFileRepository";
import { ResourceFile } from "../../../domain/entities/resources/ResourceFile";

export class ResourceFileTestRepository implements ResourceFileRepository {
uploadFile(_file: File): FutureData<Id> {
return Future.success("test-file-id");
}

downloadFile(fileId: Id): FutureData<ResourceFile> {
return Future.success({
fileId: fileId,
file: new File(["test"], "test.txt", { type: "text/plain" }),
});
}

deleteResourceFile(_fileId: Id): FutureData<void> {
return Future.success(undefined);
}
}
33 changes: 33 additions & 0 deletions src/data/repositories/test/ResourceTestRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Id } from "@eyeseetea/d2-api";
import { Future } from "../../../domain/entities/generic/Future";
import { Resource, ResourceType } from "../../../domain/entities/resources/Resource";
import { ResourceRepository } from "../../../domain/repositories/ResourceRepository";
import { FutureData } from "../../api-futures";

export class ResourceTestRepository implements ResourceRepository {
getAllResources(): FutureData<Resource[]> {
const resources: Resource[] = [
{
resourceLabel: "Incident Action Plan",
resourceType: ResourceType.TEMPLATE,
resourceFileId: "123",
},
{
resourceLabel: "Excel line list",
resourceType: ResourceType.RESPONSE_DOCUMENT,
resourceFolder: "Case line lists",
resourceFileId: "456",
},
];

return Future.success(resources);
}

saveResource(_resource: Resource): FutureData<void> {
return Future.success(undefined);
}

deleteResource(_fileId: Id): FutureData<void> {
return Future.success(undefined);
}
}
16 changes: 15 additions & 1 deletion src/domain/entities/ConfigurableForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ActionPlanAttrs } from "./incident-action-plan/ActionPlan";
import { ResponseAction } from "./incident-action-plan/ResponseAction";
import { IncidentManagementTeam } from "./incident-management-team/IncidentManagementTeam";
import { Role } from "./incident-management-team/Role";
import { Resource } from "./resources/Resource";
import { OrgUnit } from "./OrgUnit";

export type DiseaseOutbreakEventOptions = {
Expand Down Expand Up @@ -62,6 +63,11 @@ export type IncidentResponseActionOptions = {
verification: Option[];
};

export type ResourceOptions = {
resourceType: Option[];
resourceFolder: Option[];
};

export type FormLables = {
errors: Record<string, string>;
};
Expand Down Expand Up @@ -124,6 +130,13 @@ export type SingleResponseActionFormData = BaseFormData & {
options: IncidentResponseActionOptions;
};

export type ResourceFormData = BaseFormData & {
type: "resource";
entity: Maybe<Resource>;
uploadedResourceFile: Maybe<File>;
options: ResourceOptions;
};

export type IncidentManagementTeamRoleOptions = {
roles: Role[];
teamMembers: TeamMember[];
Expand All @@ -147,4 +160,5 @@ export type ConfigurableForm =
| ActionPlanFormData
| ResponseActionFormData
| SingleResponseActionFormData
| IncidentManagementTeamMemberFormData;
| IncidentManagementTeamMemberFormData
| ResourceFormData;
Loading

0 comments on commit 945ebbf

Please sign in to comment.