Skip to content

Commit

Permalink
feat: Trigger list
Browse files Browse the repository at this point in the history
  • Loading branch information
wagnercosta committed Jul 5, 2024
1 parent edcb6d5 commit 8585bc2
Show file tree
Hide file tree
Showing 14 changed files with 595 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/app/[lang]/trigger/components/TriggerList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"use server";

import { Locale, getDictionary } from "@/i18n/dictionaries";
import ConfigRowActions from "./TriggerRowActions";
import { ITable, ITableOptions } from "@/components/tables/utils";
import Table from "@/components/tables";
import ConfigPageActions from "./TriggerPageActions";

export type TriggerData = {
name: string;
type: "schedule" | "condition";
uid?: string | undefined;
created_at?: string | undefined;
updated_at?: string | undefined;
annotations?: Record<string, string> | undefined;
group?: string;
};

interface IConfigListProps {
configPromise: Promise<TriggerData[]>;
lang: Locale;
tableOptions: ITableOptions<TriggerData>;
}

const TriggerList = async (props: IConfigListProps) => {
const { lang, tableOptions, configPromise } = props;
const dict = getDictionary(lang);
const configData = await configPromise;
const localDict = dict["trigger"];
const globalDict = dict["global"];

const tableProps: ITable<TriggerData> = {
tableOptions,
data: configData,
tableHeaders: [
{
fieldName: "name",
label: localDict.name,
},
{
fieldName: "uid",
label: globalDict.uid,
},
{
fieldName: "group",
label: globalDict.group,
},
{
fieldName: "created_at",
label: globalDict.createdAt,
fieldType: "date",
},
],
RowActions: ConfigRowActions,
actionsLabel: globalDict.actions,
pathname: "config",
selectRowOptions: {
enabled: true,
fieldKey: "name",
},
pageHeaderOptions: {
title: localDict.title,
rightElement: <ConfigPageActions />,
},
};
return <Table {...tableProps} />;
};

export default TriggerList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import Modal from "@/components/common/Modal";
import useDictionaries from "@/hooks/useDictionaries";
import { useRouter } from "next/navigation";
import { useState, useTransition } from "react";
import { deleteAllConfigs } from "../../server-actions";

interface IModalDeleteSelectedConfigs {
configNames: string[];
modalDeleteAllId: string;
}
const ModalDeleteSelectedConfigs = (props: IModalDeleteSelectedConfigs) => {
const { modalDeleteAllId, configNames } = props;
const router = useRouter();
const dict = useDictionaries();
const [isPending, startTransition] = useTransition();
const [isFetching, setIsFetching] = useState(false);
const isMutating = isFetching || isPending;
const deleteAll = async () => {
setIsFetching(true);

await deleteAllConfigs(configNames);
setIsFetching(false);
startTransition(() => {
// Refresh the current route:
// - Makes a new request to the server for the route
// - Re-fetches data requests and re-renders Server Components
// - Sends the updated React Server Component payload to the client
// - The client merges the payload without losing unaffected
// client-side React state or browser state
router.refresh();

// Note: If fetch requests are cached, the updated data will
// produce the same result.
});
};

return (
<Modal
actionCallback={deleteAll}
action={dict.global.delete}
cancel={dict.global.cancel}
description={dict.configs.modalConfirmDeleteSelected}
modalId={modalDeleteAllId}
title={dict.configs.modalDeleteSelectedTitle}
isMutating={isMutating}
/>
);
};

export default ModalDeleteSelectedConfigs;
71 changes: 71 additions & 0 deletions src/app/[lang]/trigger/components/TriggerPageActions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use client";

import DropdownPageActions from "../../../../../components/DropdownPageActions";
import ModalDeleteSelectedConfigs from "./ModalDeleteSelectedConfigs";
import useDictionaries from "@/hooks/useDictionaries";
import Link from "next/link";
import { ReactNode, useContext } from "react";
import { LanguageDictType } from "@/i18n/dictionaries";
import { useRouter } from "next/navigation";
import InputTableSearch from "@/components/tables/InputTableSearch";
import { TableContext } from "@/components/tables/TableContext";
import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime";

export const getDropdownItems = (
worklowsSelected: string[],
dict: LanguageDictType,
router: AppRouterInstance,
modalDeleteAllId: string
) => {
const dropdownItems: ReactNode[] = [];
if (worklowsSelected.length > 0) {
dropdownItems.push(
<button
type="button"
key={"deleteAll"}
onClick={() => window[modalDeleteAllId].showModal()}
className="justify-between"
>
{dict.global.delete}
</button>
);
}
dropdownItems.push(
<Link href={"/config/yaml/new"} key={"newyaml"} className="justify-between">
{dict.configs.newFromYaml}
</Link>
);

dropdownItems.push(
<button className="justify-between" key={"refresh"} onClick={() => router.refresh()}>
{dict.global.refresh}
</button>
);

return dropdownItems;
};

const ConfigPageActions = () => {
const dict = useDictionaries();
const modalDeleteAllId = "delete-all-modal";
const router = useRouter();

const tableContext = useContext(TableContext);
const { allSelectedItems = [] } = tableContext || {};

const dropdownItems = getDropdownItems(allSelectedItems, dict, router, modalDeleteAllId);

return (
<div>
<InputTableSearch />
<DropdownPageActions title={dict.global.actions} dropdownItems={dropdownItems}>
<ModalDeleteSelectedConfigs
modalDeleteAllId={modalDeleteAllId}
configNames={allSelectedItems}
/>
</DropdownPageActions>
</div>
);
};

export default ConfigPageActions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"use client";

import Modal from "@/components/common/Modal";
import useDictionaries from "@/hooks/useDictionaries";
import { useRouter } from "next/navigation";
import { deleteConfig } from "../../server-actions";

interface IModalDeleteConfig {
configName: string;
modalId: string;
}
const ModalDeleteConfig = (props: IModalDeleteConfig) => {
const { configName, modalId } = props;
const router = useRouter();
const dict = useDictionaries();

const deleteConfigWithId = async () => {
await deleteConfig(configName);
router.refresh();
};

return (
<Modal
actionCallback={deleteConfigWithId}
action={dict.global.delete}
cancel={dict.global.cancel}
description={dict.configs.modalConfirmDeleteRow}
modalId={modalId}
title={dict.configs.modalDeleteRowTitle}
/>
);
};

export default ModalDeleteConfig;
30 changes: 30 additions & 0 deletions src/app/[lang]/trigger/components/TriggerRowActions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client";

import TableRowActions, { ITableRowActions } from "@/components/tables/TableRowActions";
import useDictionaries from "@/hooks/useDictionaries";
import ModalDeleteConfig from "./ModalDeleteConfig";
import { TriggerData } from "../TriggerList";

const ConfigRowActions = (props: TriggerData) => {
const { name } = props;
const dict = useDictionaries();
const modalDeleteId = `modal-delete-config-${name}`;
const actions: ITableRowActions[] = [
{
label: dict.global.delete,
modalId: modalDeleteId,
},
{
label: dict.global.editYaml,
link: `/config/yaml/edit/${name}`,
},
];

return (
<TableRowActions actions={actions}>
<ModalDeleteConfig configName={name} modalId={modalDeleteId} />
</TableRowActions>
);
};

export default ConfigRowActions;
76 changes: 76 additions & 0 deletions src/app/[lang]/trigger/components/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { notFound } from "next/navigation";
import { FLOWDAPT_API_VERSION, getClient } from "@/lib/util";
import { TriggerData } from "./TriggerList";
import { ConfigResourceReadResponseDTOType } from "@emergentmethods/flowdapt-ts-sdk";

export async function getTriggers() {
const flowdaptSDK = getClient();
const data = await flowdaptSDK.triggers.listTriggers();
return data;
}

export async function getTriggersFlatData() {
const data = await getTriggers();
const triggerData: TriggerData[] = data.map(i => {
return {
name: i.data.metadata.name,
type: i.data.spec.type,
uid: i.data.metadata.uid,
group: i.data.metadata.annotations?.group || "",
created_at: i.data.metadata?.created_at?.toISOString(),
updated_at: i.data.metadata?.updated_at?.toISOString(),
};
});
return triggerData;
}

export async function getTrigger(id: string) {
const flowdaptSDK = getClient();
try {
const mainData = await flowdaptSDK.triggers.getTrigger({
id: id,
});
return mainData.data;
} catch (e) {
return null;
}
}

export const handleConfigflowNewEditRoute = (props: Flowdapt.IPageParams) => {
const action = (props?.params?.id || [])[0];
const editId = (props?.params?.id || [])[1];

if (["edit", "new"].indexOf(action) === -1) {
notFound();
}
if (action === "new" && editId) {
notFound();
}
if (action === "edit" && !editId) {
notFound();
}
return { id: editId, type: action as "new" | "edit" };
};

export const getInitialConfigPageData = async (id: string, type: "new" | "edit") => {
if (type === "new") {
const config: ConfigResourceReadResponseDTOType[FLOWDAPT_API_VERSION]["data"] = {
kind: "config",
metadata: {
name: "",
annotations: {},
},
spec: {
selector: {
type: "annotation",
value: "",
},
data: {},
},
};
return config;
} else {
const config = await getTrigger(id);
return config;
}
};
26 changes: 26 additions & 0 deletions src/app/[lang]/trigger/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import { getLanguage } from "@/lib/util";
import TriggerList, { TriggerData } from "./components/TriggerList";
import { filterOrderDataTable, getTableOptions, ITableOptions } from "@/components/tables/utils";
import Container from "@/components/Container";
import { getTriggersFlatData } from "./components/utils";

export const revalidate = 0;

const getTriggersPageData = async (tableOptions: ITableOptions<TriggerData>) => {
const configList: TriggerData[] = await getTriggersFlatData();
const filteredConfigs: TriggerData[] = await filterOrderDataTable(tableOptions, configList);
return filteredConfigs;
};

export default async function Page(props: Flowdapt.IPageParams) {
const lang = getLanguage(props);
const tableOptions = getTableOptions<TriggerData>(props, ["name", "uid", "group"]);
const data = getTriggersPageData(tableOptions);

return (
<Container>
<TriggerList configPromise={data} tableOptions={tableOptions} lang={lang} />
</Container>
);
}
28 changes: 28 additions & 0 deletions src/app/[lang]/trigger/server-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use server";

import { getClient } from "@/lib/util";

export async function saveConfig(definition: any, identifier?: string) {
const flowdaptSDK = getClient();

if (identifier) {
await flowdaptSDK.configs.updateConfig({
request: definition,
identifier: identifier,
});
} else {
await flowdaptSDK.configs.createConfig({
request: definition,
});
}
}

export async function deleteConfig(identifier: string) {
const flowdaptSDK = getClient();
await flowdaptSDK.configs.deleteConfig({ identifier: identifier });
}

export async function deleteAllConfigs(identifierList: string[]) {
const flowdaptSDK = getClient();
await Promise.all(identifierList.map(w => flowdaptSDK.configs.deleteConfig({ identifier: w })));
}
Loading

0 comments on commit 8585bc2

Please sign in to comment.