Skip to content

Commit

Permalink
fix(ui): incompatible options (#58)
Browse files Browse the repository at this point in the history
When switching from one target to another, some incompatible values were not removed properly.

Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com>
  • Loading branch information
raphaelcoeffic and raphaelcoeffic committed Apr 6, 2024
1 parent d6c79d3 commit e142e11
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 73 deletions.
5 changes: 2 additions & 3 deletions targets.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"language": {
"build_flag": "TRANSLATIONS",
"values": [
"EN",
"CZ",
"DA",
"DE",
Expand All @@ -42,12 +41,12 @@
"FR",
"HU",
"IT",
"NL",
"PL",
"PT",
"RU",
"SK",
"SE",
"PL",
"NL",
"UA"
]
},
Expand Down
161 changes: 91 additions & 70 deletions ui/src/components/jobs/JobCreateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import {
Space,
Upload,
} from "antd";
import { FormInstance, useForm } from "antd/es/form/Form";
import { useEffect, useMemo, useState } from "react";
import { useForm } from "antd/es/form/Form";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DefaultOptionType } from "antd/es/select";
import { MessageInstance } from "antd/es/message/interface";

Expand All @@ -25,52 +25,44 @@ function mapToSelect(values: string[]) {
}

interface FormTagProps {
flags: Record<string, TargetFlag>;
form: FormInstance<JobCreationParams>;
value: FormListFieldData;
index: number;
flags: Record<string, TargetFlag>;
selectedFlags: Flag[];
updateSelectedFlags: (flags: Flag[]) => void;
remove: (index: number | number[]) => void;
}

function FormTag({ flags, form, value, index, remove }: FormTagProps) {
const [currentFlag, setCurrentFlag] = useState("");
const [currentValue, setCurrentValue] = useState("");
function FormTag({ value, flags, selectedFlags, updateSelectedFlags, remove }: FormTagProps) {
const currentFlag = selectedFlags?.at(value.key)?.name;
const currentValue = selectedFlags?.at(value.key)?.value;

// selected flags
const selectedFlags = useMemo(
() => form.getFieldsValue()?.flags ?? [],
[form]
);
const selectedFlagsName = new Set(selectedFlags?.map((flag) => flag.name));

// current flag not in flag list, then remove it
useEffect(() => {
if (currentFlag == "" || Object.hasOwn(flags, currentFlag)) return;
remove(index);
}, [currentFlag, flags, form, selectedFlags, index, remove]);
// remove already selected flags from options
const flagNames = Object.keys(flags).sort();
const flagOptions = mapToSelect(flagNames).filter(
(option) => !selectedFlagsName.has(option.label)
);

// flag values
const values = [...new Set(flags[currentFlag]?.values)];
values.sort();

// current value not in flag value? reset it
if (currentValue && !values.includes(currentValue)) {
selectedFlags[index].value = "";
}

// selected flags
const selectedFlagsName = new Set(
selectedFlags
.filter((flag: { name?: string }) => flag?.name)
.map((flag: { name: string }) => flag.name)
const flagValuesSet = useMemo(
() => new Set(flags[currentFlag ?? ""]?.values),
[flags, currentFlag]
);

// remove already selected flags from options
const flagKeys = Object.keys(flags);
flagKeys.sort();
const flagOptions = mapToSelect(flagKeys).filter(
(option) => !selectedFlagsName.has(option.label)
);
const valueOptions = mapToSelect(values);
const flagValues = mapToSelect([...flagValuesSet].sort());

// reset the flag value if the target or flag name doesn't support it
useEffect(() => {
if (!currentValue || flagValuesSet.size === 0) return;
if (!flagValuesSet.has(currentValue)) {
const newSelectedFlags = selectedFlags.map((flag) => ({
name: flag.name,
value: flag.name === currentFlag ? "" : flag.value,
}));
updateSelectedFlags(newSelectedFlags);
}
}, [flagValuesSet, currentFlag, currentValue, selectedFlags, updateSelectedFlags]);

return (
<div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
Expand All @@ -88,7 +80,6 @@ function FormTag({ flags, form, value, index, remove }: FormTagProps) {
<Select
showSearch
placeholder="Flag"
onChange={(flag) => setCurrentFlag(flag)}
options={flagOptions}
/>
</Form.Item>
Expand All @@ -101,8 +92,7 @@ function FormTag({ flags, form, value, index, remove }: FormTagProps) {
<Select
showSearch
placeholder="Value"
onChange={(value) => setCurrentValue(value)}
options={valueOptions}
options={flagValues}
/>
</Form.Item>
</Space.Compact>
Expand Down Expand Up @@ -155,37 +145,65 @@ function JobCreateForm({ messageApi, onFinish }: Props) {
// set target to first target of cloudbuild targets
useEffect(() => {
if (!targets) return;
let releaseTargets = Object.keys(targets.targets);
const excludeTargets = targets.releases[currentRelease]?.exclude_targets;

if (excludeTargets) {
releaseTargets = releaseTargets.filter(
(target) => !excludeTargets.includes(target)
);
}
const releaseTargets = Object.keys(targets.targets).filter(
(target) => !excludeTargets?.includes(target)
);

setTargetOptions(mapToSelect(releaseTargets));
form.setFieldValue("target", releaseTargets[0]);
setCurrentTarget(releaseTargets[0]);
}, [targets, currentRelease, form]);

// flags
const flags = structuredClone(targets?.flags) ?? {};

// get additional flags from the target tags
const tags = targets?.targets[currentTarget]?.tags;
const additionalFlags: Record<string, TargetFlag> =
tags?.reduce((obj, tag) => {
obj = { ...targets?.tags[tag].flags, ...obj };
return obj;
}, {}) ?? {};

// add new flags defined in the tags in the flags list
for (const [key, value] of Object.entries(additionalFlags)) {
if (!Object.hasOwn(flags, key)) {
flags[key] = value;
const flags = useMemo(
() => {
const flags = structuredClone(targets?.flags) ?? {};

// get additional flags from the target tags
const tags = targets?.targets[currentTarget]?.tags;
const additionalFlags: Record<string, TargetFlag> =
tags?.reduce((obj, tag) => {
obj = { ...targets?.tags[tag].flags, ...obj };
return obj;
}, {}) ?? {};

// add new flags defined in the tags in the flags list
for (const [key, value] of Object.entries(additionalFlags)) {
if (!Object.hasOwn(flags, key)) {
flags[key] = value;
} else {
const valueSet = new Set(flags[key].values)
value.values.forEach((v) => valueSet.add(v));
flags[key].values = Array.from(valueSet.values());
}
}
return flags;
},
[targets, currentTarget]
);

const [selectedFlags, setSelectedFlags] = useState<Flag[]>([]);
const updateSelectedFlags = useCallback(
(flags: Flag[]) => {
setSelectedFlags(flags);
form.setFieldValue("flags", flags);
},
[form]
);

// remove selected flag if it's not in the flags anymore
useEffect(() => {
if (!flags || !selectedFlags) return;

const flagIds = new Set(Object.keys(flags));
const newSelectedFlags = selectedFlags.filter(
(flag) => !flag.name || flagIds.has(flag.name)
);
if (newSelectedFlags.length !== selectedFlags.length) {
updateSelectedFlags(newSelectedFlags);
}
}
}, [flags, selectedFlags, updateSelectedFlags]);

// file list and file content for upload file form input
const [fileList, setFileList] = useState<File[]>([]);
Expand All @@ -211,7 +229,7 @@ function JobCreateForm({ messageApi, onFinish }: Props) {
setFileList([]);
};

const toUploadFiles = (files: File[]) => files.map((f) => ({ uid: f.name, name: f.name}));
const toUploadFiles = (files: File[]) => files.map((f) => ({ uid: f.name, name: f.name }));

return (
<>
Expand All @@ -220,6 +238,9 @@ function JobCreateForm({ messageApi, onFinish }: Props) {
aria-labelledby=""
form={form}
name="Job Creation"
onValuesChange={(_, values) => {
setSelectedFlags(values.flags);
}}
onFinish={(values) => {
onFinish(values, jobsFileContent);
}}
Expand Down Expand Up @@ -248,17 +269,17 @@ function JobCreateForm({ messageApi, onFinish }: Props) {
/>
</Form.Item>

<Form.List name="flags" initialValue={[]}>
<Form.List name="flags" initialValue={selectedFlags}>
{(fields, { add, remove }) => (
<Form.Item label="Flags">
{fields.map((value, index) => (
{fields.map((value) => (
<div key={value.key}>
<FormTag
{...{
flags,
form,
value,
index,
flags,
selectedFlags,
updateSelectedFlags,
remove,
}}
/>
Expand All @@ -268,7 +289,7 @@ function JobCreateForm({ messageApi, onFinish }: Props) {
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
onClick={() => add({ name: undefined, value: undefined })}
block
icon={<PlusOutlined />}
>
Expand Down

0 comments on commit e142e11

Please sign in to comment.