Turned the mailtypes ul into a reusable selectable list control.
This commit is contained in:
parent
1e16b2676e
commit
745473759d
@ -11,26 +11,3 @@
|
||||
grid-template-columns: fit-content(50%) auto;
|
||||
grid-gap: $gridGap;
|
||||
}
|
||||
|
||||
.mail-types {
|
||||
padding-left: 0rem;
|
||||
list-style: none;
|
||||
width: $mailtemplateNameListWidth;
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
@import "./_expandableCell.scss";
|
||||
@import "./_errorLogs.scss";
|
||||
@import "./addTaskButton.scss";
|
||||
@import "./selectableList.scss";
|
||||
|
||||
//Changes needed to make MS Edge behave the same as other browsers
|
||||
input::-ms-reveal {
|
||||
|
||||
22
src/Sass/selectableList.scss
Normal file
22
src/Sass/selectableList.scss
Normal file
@ -0,0 +1,22 @@
|
||||
.selectable-list {
|
||||
padding-left: 0rem;
|
||||
list-style: none;
|
||||
width: $mailtemplateNameListWidth;
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/components/common/SelectableList.tsx
Normal file
33
src/components/common/SelectableList.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import React from "react";
|
||||
|
||||
export interface SelectableListProps<T> {
|
||||
items: T[];
|
||||
selectedValue?: t | null;
|
||||
renderLabel: (item: T) => React.ReactNode;
|
||||
onSelect: (item: T) => void;
|
||||
}
|
||||
|
||||
export const SelectableList = <T,>(
|
||||
props: SelectableListProps<T>,
|
||||
): JSX.Element => {
|
||||
const { items, selectedValue, renderLabel, onSelect } = props;
|
||||
|
||||
const listClassName = "selectable-list";
|
||||
|
||||
return (
|
||||
<ul className={listClassName}>
|
||||
{items.map((item, index) => {
|
||||
const isSelected = selectedValue === item;
|
||||
const className = isSelected
|
||||
? ["selected"].filter(Boolean).join(" ")
|
||||
: "";
|
||||
|
||||
return (
|
||||
<li key={index} onClick={() => onSelect(item)} className={className}>
|
||||
{renderLabel(item)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
@ -102,6 +102,7 @@ export const useForm = (initialState: FormState): UseFormReturn => {
|
||||
const initialDataRef = useRef<FormData>(
|
||||
JSON.parse(JSON.stringify(initialState.data)),
|
||||
);
|
||||
const wasLoadedRef = useRef<boolean>(initialState.loaded);
|
||||
|
||||
const setState = useCallback((updates: Partial<FormState>) => {
|
||||
setStateInternal((prev) => ({ ...prev, ...updates }));
|
||||
@ -621,6 +622,14 @@ export const useForm = (initialState: FormState): UseFormReturn => {
|
||||
return cleanup;
|
||||
}, [setupNavigationGuard]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wasLoadedRef.current && state.loaded) {
|
||||
initialDataRef.current = JSON.parse(JSON.stringify(state.data));
|
||||
}
|
||||
|
||||
wasLoadedRef.current = state.loaded;
|
||||
}, [state.loaded, state.data]);
|
||||
|
||||
const api: any = {
|
||||
state,
|
||||
schema: schemaRef.current,
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import React, { useEffect, useState, useCallback, Suspense } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import mailTemplatesService from "../serrvices/mailTemplatesService";
|
||||
import HOCEmailTemplateEditor from "./EmailTemplateEditor";
|
||||
import Loading from "../../../../components/common/Loading";
|
||||
import { Namespaces } from "../../../../i18n/i18n";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SelectableList } from "../../../../components/common/SelectableList";
|
||||
|
||||
interface MailType {
|
||||
mailType: string;
|
||||
@ -13,35 +14,27 @@ interface MailType {
|
||||
|
||||
const MailTemplatesTabContent: React.FC<{
|
||||
types: MailType[];
|
||||
currentMailType: string;
|
||||
currentMailType: MailType | null;
|
||||
domainId: string | undefined;
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => void;
|
||||
onClick: (item: MailType) => void;
|
||||
}> = ({ types, currentMailType, domainId, onClick }) => {
|
||||
const { t: tMail } = useTranslation(Namespaces.MailTypes);
|
||||
|
||||
return (
|
||||
<div className="two-column-grid">
|
||||
<div className="fit-content-width">
|
||||
<ul className="mail-types">
|
||||
{types.map((x) => {
|
||||
return (
|
||||
<li
|
||||
key={x.mailType}
|
||||
value={x.mailType}
|
||||
onClick={onClick}
|
||||
className={currentMailType === x.mailType ? "selected" : ""}
|
||||
>
|
||||
{tMail(x.mailType)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<SelectableList
|
||||
items={types}
|
||||
selectedValue={currentMailType}
|
||||
renderLabel={(x) => tMail(x.mailType)}
|
||||
onSelect={(item) => onClick(item)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
{domainId && currentMailType ? (
|
||||
<HOCEmailTemplateEditor
|
||||
domainId={domainId}
|
||||
currentMailType={currentMailType}
|
||||
currentMailType={currentMailType.mailType}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
@ -51,7 +44,7 @@ const MailTemplatesTabContent: React.FC<{
|
||||
|
||||
const MailTemplatesTab: React.FC = () => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [currentMailType, setCurrentMailType] = useState("");
|
||||
const [currentMailType, setCurrentMailType] = useState<MailType | null>(null);
|
||||
const [types, setTypes] = useState<MailType[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -62,7 +55,7 @@ const MailTemplatesTab: React.FC = () => {
|
||||
setTypes(nextTypes);
|
||||
|
||||
if (nextTypes.length > 0) {
|
||||
setCurrentMailType(nextTypes[0].mailType);
|
||||
setCurrentMailType(nextTypes[0]);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
@ -74,34 +67,20 @@ const MailTemplatesTab: React.FC = () => {
|
||||
void loadTypes();
|
||||
}, []);
|
||||
|
||||
const selectTemplate = useCallback(
|
||||
(emailType: string) => {
|
||||
if (currentMailType !== emailType) {
|
||||
setCurrentMailType(emailType);
|
||||
}
|
||||
},
|
||||
[currentMailType],
|
||||
);
|
||||
|
||||
const onClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
const value = (e.target as HTMLElement).getAttribute("value");
|
||||
if (value) {
|
||||
selectTemplate(value);
|
||||
}
|
||||
const onClick = (item: MailType) => {
|
||||
setCurrentMailType(item);
|
||||
};
|
||||
|
||||
const { domainId } = useParams<{ domainId: string }>();
|
||||
|
||||
return (
|
||||
<Loading loaded={loaded}>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<MailTemplatesTabContent
|
||||
types={types}
|
||||
currentMailType={currentMailType}
|
||||
domainId={domainId}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</Suspense>
|
||||
<MailTemplatesTabContent
|
||||
types={types}
|
||||
currentMailType={currentMailType}
|
||||
domainId={domainId}
|
||||
onClick={onClick}
|
||||
/>
|
||||
</Loading>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
TaskDefinition,
|
||||
TaskMetadata,
|
||||
@ -6,6 +6,7 @@ import {
|
||||
import AddTaskButton from "./AddTaskButton";
|
||||
import { Namespaces } from "../../../../i18n/i18n";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { SelectableList } from "../../../../components/common/SelectableList";
|
||||
|
||||
interface TaskListProps {
|
||||
tasks: TaskDefinition[];
|
||||
@ -16,14 +17,30 @@ interface TaskListProps {
|
||||
const TaskList: React.FC<TaskListProps> = ({ tasks, taskType, onChange }) => {
|
||||
const { t: tTaskType } = useTranslation(Namespaces.TaskTypes);
|
||||
|
||||
const [currentTask, setCurrentTask] = useState<TaskDefinition | null>(null);
|
||||
|
||||
const formatNewTaskName = (
|
||||
displayName: string,
|
||||
tasks: TaskDefinition<Record<string, unknown>>[],
|
||||
) => {
|
||||
return `${tTaskType(displayName)} ${tasks.length + 1}`;
|
||||
};
|
||||
|
||||
const handleAddTask = (selectedType: TaskMetadata) => {
|
||||
const formattedName = formatNewTaskName(selectedType.displayName, tasks);
|
||||
|
||||
const newTask: TaskDefinition = {
|
||||
type: selectedType.taskType,
|
||||
|
||||
config: { name: tTaskType(selectedType.displayName) },
|
||||
config: {
|
||||
name: formattedName,
|
||||
guid: crypto.randomUUID(),
|
||||
},
|
||||
};
|
||||
|
||||
console.log("Add Task clicked");
|
||||
if (tasks.length === 0) {
|
||||
setCurrentTask(newTask);
|
||||
}
|
||||
onChange([...tasks, newTask]);
|
||||
};
|
||||
|
||||
@ -31,11 +48,12 @@ const TaskList: React.FC<TaskListProps> = ({ tasks, taskType, onChange }) => {
|
||||
<div>
|
||||
<AddTaskButton taskType={taskType} onAdd={handleAddTask} />
|
||||
|
||||
<ul>
|
||||
{tasks.map((task, index) => (
|
||||
<li key={index}>{task.type}</li>
|
||||
))}
|
||||
</ul>
|
||||
<SelectableList
|
||||
items={tasks}
|
||||
selectedValue={currentTask}
|
||||
renderLabel={(x) => x.config.name as string}
|
||||
onSelect={(item) => setCurrentTask(item)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -30,7 +30,7 @@ export type ReadWorkflowTemplateVersion = {
|
||||
|
||||
export interface TaskDefinition<TConfig = Record<string, unknown>> {
|
||||
type: string;
|
||||
config?: TConfig;
|
||||
config: TConfig;
|
||||
}
|
||||
|
||||
export interface CreateWorkflowTemplateVersion extends FormData {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user