Skip to content

Commit

Permalink
feat: migration
Browse files Browse the repository at this point in the history
Added migration routing in the scorecard list page
  • Loading branch information
nnkogift committed Oct 1, 2024
1 parent e08806f commit 37b288f
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions packages/app/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: 2024-09-30T13:38:08.282Z\n"
"PO-Revision-Date: 2024-09-30T13:38:08.282Z\n"
"POT-Creation-Date: 2024-10-01T09:23:10.631Z\n"
"PO-Revision-Date: 2024-10-01T09:23:10.631Z\n"

msgid "Scorecard deleted successfully"
msgstr "Scorecard deleted successfully"
Expand Down Expand Up @@ -461,6 +461,9 @@ msgstr "A scorecard needs at least one data group"
msgid "You must select at least one organisation unit"
msgstr "You must select at least one organisation unit"

msgid "{{count}} scorecards successfully migrated"
msgstr "{{count}} scorecards successfully migrated"

msgid "Migrating scorecard(s)... ({{progress}}/{{count}})"
msgid_plural "Migrating scorecard(s)... ({{progress}}/{{count}})"
msgstr[0] "Migrating scorecard(s)... ({{progress}}/{{count}})"
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
"A data group must have at least one data item": "A data group must have at least one data item",
"A scorecard needs at least one data group": "A scorecard needs at least one data group",
"You must select at least one organisation unit": "You must select at least one organisation unit",
"{{count}} scorecards successfully migrated": "{{count}} scorecards successfully migrated",
"Migrating scorecard(s)... ({{progress}}/{{count}})": "Migrating scorecard(s)... ({{progress}}/{{count}})",
"Migrating scorecard(s)... ({{progress}}/{{count}})_plural": "Migrating scorecard(s)... ({{progress}}/{{count}})",
"Migrate {{count}} scorecard(s)": "Migrate {{count}} scorecard(s)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ export default function EmptyScoreCardList() {
<div
style={{
alignItems: "center",
justifyContent: "center",
justifyContent: "center"
}}
className="center column align-center"
>
<div style={{ minWidth: "50dvw", maxWidth: "80dvw" }}>
<Card>
<div
className="container-bordered text-center center column background-white"
className="container-bordered text-center center align-items-center column background-white"
style={{ borderRadius: 8, padding: 32 }}
>
<div className="p-32">
<ScorecardIllustration />
<ScorecardIllustration height={200} width={200} />
</div>
<div className="p-8">
<h1
Expand All @@ -39,11 +39,11 @@ export default function EmptyScoreCardList() {
style={{
fontStyle: "italic",
color: colors.grey700,
position: "relative",
position: "relative"
}}
>
{i18n.t(
"Create a scorecard instantly, over tea break",
"Create a scorecard instantly, over tea break"
)}
...
</p>
Expand Down
10 changes: 6 additions & 4 deletions packages/app/src/modules/ScorecardList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import { useAlert } from "@dhis2/app-runtime";
import { useNavigate } from "react-router-dom";
import { ScorecardListArea } from "./components/ScorecardListArea";
import { ErrorBoundary } from "react-error-boundary";
import { useAutoMigration } from "../ScorecardMigration/hooks/autoMigration";
import { MigrationNavigateButton } from "../ScorecardMigration/components/MigrationNavigateButton";

export default function ScorecardList() {
useAutoMigration();
const [scorecardViewType, { set }] = useSetting("scorecardViewType");
const [helpEnabled, setHelpEnabled] = useState<boolean>(false);
const { show } = useAlert(
Expand Down Expand Up @@ -54,8 +53,8 @@ export default function ScorecardList() {
onExit={onHelpExit}
initialStep={0}
/>
<div className="column h-100">
<div className="row p-16">
<div className="column h-100 p-16">
<div className="row">
<div
className="row p-45 center"
style={{ paddingLeft: "35%" }}
Expand Down Expand Up @@ -111,6 +110,9 @@ export default function ScorecardList() {
<ScorecardListArea />
</ErrorBoundary>
</div>
<div className="row end">
<MigrationNavigateButton />
</div>
</div>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { CircularLoader } from "@dhis2/ui";
import i18n from "@dhis2/d2-i18n";
import { DATASTORE_OLD_SCORECARD_ENDPOINT } from "@scorecard/shared";
import { useDataQuery } from "@dhis2/app-runtime";
import { isEmpty } from "lodash";
import { Link } from "react-router-dom";


const query = {
data: {
resource: `${DATASTORE_OLD_SCORECARD_ENDPOINT}`
}
};

type Response = {
data: string[]
}

export function MigrationNavigateButton() {
const { data, loading } = useDataQuery<Response>(query);

if (loading) {
return (
<CircularLoader extrasmall />
);
}

if (data && !isEmpty(data)) {
return (
<Link style={{
fontStyle: "italic"
}} to={`/migrate`}>
{i18n.t("Migrate scorecards from v1")}
</Link>
);
}


return null;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useOldScorecards } from "../hooks/data";
import { FullPageError } from "@scorecard/shared";
import { CircularLoader } from "@dhis2/ui";
import { CircularLoader, Tag } from "@dhis2/ui";
import { SimpleDataTable, SimpleDataTableColumn } from "@hisptz/dhis2-ui";
import i18n from "@dhis2/d2-i18n";
import { useMemo, useState } from "react";
import { uniq } from "lodash";
import { useEffect, useMemo, useState } from "react";
import { fromPairs, get, uniq } from "lodash";
import { MigrateButton } from "./MigrateButton";


Expand All @@ -16,15 +16,20 @@ const columns: SimpleDataTableColumn[] = [
{
key: "description",
label: i18n.t("Description")
},
{
key: "status",
label: i18n.t("Status")
}

];

export function OldScorecardList() {
const { loading, scorecards, error, refetch } = useOldScorecards();
const { loading, scorecards, error, refetch, existingScorecards } = useOldScorecards();
const [selectedConfig, setSelectedConfig] = useState<string[]>([]);
const [progress, setProgress] = useState<Record<string, "SUCCESS" | "EXISTS" | "FAILED">>({});


const onRemove = (values: string[]) => {
setSelectedConfig((prevState) => {
return prevState.filter((value) => !values.includes(value));
Expand All @@ -35,15 +40,39 @@ export function OldScorecardList() {
setSelectedConfig((prevState) => uniq([...prevState, ...values]));
};

const getStatus = (status?: "SUCCESS" | "EXISTS" | "FAILED") => {
switch (status) {
case "SUCCESS":
return <Tag bold positive>{i18n.t("Success")}</Tag>;
case "EXISTS":
return <Tag bold positive>{i18n.t("Migrated")}</Tag>;
case "FAILED":
return <Tag bold negative>{i18n.t("Failed")}</Tag>;
default:
return "";
}
};

const rows = useMemo(() => scorecards?.map((config) => {

return {
id: config.id,
title: config.header.title,
description: config.header.description
description: config.header.description,
status: getStatus(get(progress, config.id)),
cellsStyle: {
bordered: true,
selectable: get(progress, config.id) === "EXISTS"
}
};
}) ?? [], [scorecards]);

useEffect(() => {
if (existingScorecards) {
setProgress(fromPairs(existingScorecards.map((id) => ([id, "EXISTS"]))));
}
}, [existingScorecards]);

if (loading) {
return <div className="column center items-center w-100 h-100 flex-1">
<CircularLoader />
Expand All @@ -60,7 +89,7 @@ export function OldScorecardList() {

return (
<div className="column w-100 h-100 flex-1 gap-16 pb-32">
<SimpleDataTable tableProps={{ scrollHeight: "100%" }} onRowDeselect={onRemove} selectedRows={selectedConfig} onRowSelect={onAdd} selectable columns={columns} rows={rows} />
<SimpleDataTable tableProps={{ scrollHeight: "100%", bordered: true }} onRowDeselect={onRemove} selectedRows={selectedConfig} onRowSelect={onAdd} selectable columns={columns} rows={rows} />
<div className="row end">
{
!!scorecards && (<MigrateButton onClearSelection={() => setSelectedConfig([])} progress={progress} onProgressUpdate={setProgress} selected={selectedConfig} scorecards={scorecards!} />)
Expand Down
11 changes: 9 additions & 2 deletions packages/app/src/modules/ScorecardMigration/hooks/data.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DATASTORE_OLD_SCORECARD_ENDPOINT } from "@scorecard/shared";
import { DATASTORE_NAMESPACE, DATASTORE_OLD_SCORECARD_ENDPOINT } from "@scorecard/shared";
import { OldScorecardSchema } from "../schemas/old";
import { useDataQuery } from "@dhis2/app-runtime";
import { Pager } from "@hisptz/dhis2-ui";
Expand All @@ -11,6 +11,9 @@ const query = {
fields: ".",
paging: false
}
},
newConfig: {
resource: `dataStore/${DATASTORE_NAMESPACE}`
}
};

Expand All @@ -21,7 +24,8 @@ type Response = {
value: OldScorecardSchema
}>,
pager: Pager
}
},
newConfig: string[]
}

export function useOldScorecards() {
Expand All @@ -32,8 +36,11 @@ export function useOldScorecards() {
id: key
}));

const existingScorecards = data?.newConfig ?? [];

return {
scorecards,
existingScorecards,
loading,
error,
refetch
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback } from "react";
import { OldScorecardSchema } from "../schemas/old";
import { FetchError, useDataEngine, useDataQuery } from "@dhis2/app-runtime";
import { FetchError, useDataEngine } from "@dhis2/app-runtime";
import { DATASTORE_NAMESPACE, migrateScorecard } from "@scorecard/shared";
import { useSaveScorecard } from "../../ScorecardManagement/hooks/save";

Expand All @@ -15,9 +15,6 @@ const newScorecardConfigQuery: any = {
export function useMigrateScorecard() {
const engine = useDataEngine();
const { saveSilently } = useSaveScorecard();
const { refetch } = useDataQuery(newScorecardConfigQuery, {
lazy: true
});

const createNewConfiguration = useCallback(async (oldScorecard: OldScorecardSchema) => {
return migrateScorecard({ oldScorecard, engine });
Expand All @@ -28,9 +25,14 @@ export function useMigrateScorecard() {
const config = await createNewConfiguration(oldScorecard);
//check if the scorecard exists
try {
await refetch({ id: config.id });
await engine.query(newScorecardConfigQuery, {
variables: {
id: config.id
}
});
return "EXISTS";
} catch (error) {
console.log(error);
try {
if (error instanceof FetchError) {
//My bad, It's not there. Let's put it
Expand Down
9 changes: 8 additions & 1 deletion packages/app/src/modules/ScorecardMigration/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import React from "react";
import i18n from "@dhis2/d2-i18n";
import { OldScorecardList } from "./components/OldScorecardList";
import { Button, IconArrowLeft24 } from "@dhis2/ui";
import { useNavigate } from "react-router-dom";

export default function ScorecardMigration() {

const navigate = useNavigate();
return (
<div className="column h-100 w-100 p-32 gap-24">
<div>
<Button onClick={() => navigate(-1)} icon={<IconArrowLeft24 />}>
{i18n.t("Back")}
</Button>
</div>
<div>
<h2>{i18n.t("Scorecards to migrate")}</h2>
<span>{i18n.t("Select the scorecards configuration that you would like to migrate")}</span>
Expand Down

0 comments on commit 37b288f

Please sign in to comment.