Merge pull request #28 from amhsirak/develop
feat: list step field modification
This commit is contained in:
@@ -214,7 +214,7 @@ export const BrowserWindow = () => {
|
|||||||
const newField: TextStep = {
|
const newField: TextStep = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
label: `Label ${Object.keys(fields).length + 1}`,
|
label: ``,
|
||||||
data: data,
|
data: data,
|
||||||
selectorObj: {
|
selectorObj: {
|
||||||
selector: highlighterData.selector,
|
selector: highlighterData.selector,
|
||||||
@@ -274,7 +274,7 @@ export const BrowserWindow = () => {
|
|||||||
const newField: TextStep = {
|
const newField: TextStep = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
label: `Label ${Object.keys(fields).length + 1}`,
|
label: ``,
|
||||||
data: data,
|
data: data,
|
||||||
selectorObj: {
|
selectorObj: {
|
||||||
selector: selectedElement.selector,
|
selector: selectedElement.selector,
|
||||||
|
|||||||
@@ -19,29 +19,34 @@ import Radio from '@mui/material/Radio';
|
|||||||
import RadioGroup from '@mui/material/RadioGroup';
|
import RadioGroup from '@mui/material/RadioGroup';
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// 1. Handle field label update
|
// 1. Add description for each browser step
|
||||||
// 2. Handle field deletion | confirmation
|
// 2. Handle non custom action steps
|
||||||
// 3. Add description for each browser step
|
|
||||||
// 4. Handle non custom action steps
|
|
||||||
interface RightSidePanelProps {
|
interface RightSidePanelProps {
|
||||||
onFinishCapture: () => void;
|
onFinishCapture: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture }) => {
|
export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture }) => {
|
||||||
const [textLabels, setTextLabels] = useState<{ [id: number]: string }>({});
|
const [textLabels, setTextLabels] = useState<{ [id: string]: string }>({});
|
||||||
const [errors, setErrors] = useState<{ [id: number]: string }>({});
|
const [errors, setErrors] = useState<{ [id: string]: string }>({});
|
||||||
const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: number]: boolean }>({});
|
const [confirmedTextSteps, setConfirmedTextSteps] = useState<{ [id: string]: boolean }>({});
|
||||||
|
const [confirmedListTextFields, setConfirmedListTextFields] = useState<{ [listId: string]: { [fieldKey: string]: boolean } }>({});
|
||||||
const [showPaginationOptions, setShowPaginationOptions] = useState(false);
|
const [showPaginationOptions, setShowPaginationOptions] = useState(false);
|
||||||
const [showLimitOptions, setShowLimitOptions] = useState(false);
|
const [showLimitOptions, setShowLimitOptions] = useState(false);
|
||||||
const [captureStage, setCaptureStage] = useState<'initial' | 'pagination' | 'limit' | 'complete'>('initial');
|
const [captureStage, setCaptureStage] = useState<'initial' | 'pagination' | 'limit' | 'complete'>('initial');
|
||||||
|
|
||||||
const { lastAction, notify } = useGlobalInfoStore();
|
const { lastAction, notify } = useGlobalInfoStore();
|
||||||
const { getText, startGetText, stopGetText, getScreenshot, startGetScreenshot, stopGetScreenshot, paginationMode, getList, startGetList, stopGetList, startPaginationMode, stopPaginationMode, paginationType, updatePaginationType, limitMode, limitType, customLimit, updateLimitType, updateCustomLimit, stopLimitMode, startLimitMode } = useActionContext();
|
const { getText, startGetText, stopGetText, getScreenshot, startGetScreenshot, stopGetScreenshot, paginationMode, getList, startGetList, stopGetList, startPaginationMode, stopPaginationMode, paginationType, updatePaginationType, limitMode, limitType, customLimit, updateLimitType, updateCustomLimit, stopLimitMode, startLimitMode } = useActionContext();
|
||||||
const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep } = useBrowserSteps();
|
const { browserSteps, updateBrowserTextStepLabel, deleteBrowserStep, addScreenshotStep, updateListTextFieldLabel, removeListTextField } = useBrowserSteps();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
|
|
||||||
const handleTextLabelChange = (id: number, label: string) => {
|
const handleTextLabelChange = (id: number, label: string, listId?: number, fieldKey?: string) => {
|
||||||
setTextLabels(prevLabels => ({ ...prevLabels, [id]: label }));
|
if (listId !== undefined && fieldKey !== undefined) {
|
||||||
|
// This is a text field within a list step
|
||||||
|
updateListTextFieldLabel(listId, fieldKey, label);
|
||||||
|
} else {
|
||||||
|
// This is a standalone text step
|
||||||
|
setTextLabels(prevLabels => ({ ...prevLabels, [id]: label }));
|
||||||
|
}
|
||||||
if (!label.trim()) {
|
if (!label.trim()) {
|
||||||
setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' }));
|
setErrors(prevErrors => ({ ...prevErrors, [id]: 'Label cannot be empty' }));
|
||||||
} else {
|
} else {
|
||||||
@@ -71,6 +76,32 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleListTextFieldConfirm = (listId: number, fieldKey: string) => {
|
||||||
|
setConfirmedListTextFields(prev => ({
|
||||||
|
...prev,
|
||||||
|
[listId]: {
|
||||||
|
...(prev[listId] || {}),
|
||||||
|
[fieldKey]: true
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleListTextFieldDiscard = (listId: number, fieldKey: string) => {
|
||||||
|
removeListTextField(listId, fieldKey);
|
||||||
|
setConfirmedListTextFields(prev => {
|
||||||
|
const updatedListFields = { ...(prev[listId] || {}) };
|
||||||
|
delete updatedListFields[fieldKey];
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
[listId]: updatedListFields
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setErrors(prev => {
|
||||||
|
const { [fieldKey]: _, ...rest } = prev;
|
||||||
|
return rest;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const getTextSettingsObject = useCallback(() => {
|
const getTextSettingsObject = useCallback(() => {
|
||||||
const settings: Record<string, { selector: string; tag?: string;[key: string]: any }> = {};
|
const settings: Record<string, { selector: string; tag?: string;[key: string]: any }> = {};
|
||||||
browserSteps.forEach(step => {
|
browserSteps.forEach(step => {
|
||||||
@@ -132,6 +163,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
|||||||
return settings;
|
return settings;
|
||||||
}, [browserSteps, paginationType, limitType, customLimit]);
|
}, [browserSteps, paginationType, limitType, customLimit]);
|
||||||
|
|
||||||
|
|
||||||
const resetListState = useCallback(() => {
|
const resetListState = useCallback(() => {
|
||||||
setShowPaginationOptions(false);
|
setShowPaginationOptions(false);
|
||||||
updatePaginationType('');
|
updatePaginationType('');
|
||||||
@@ -369,7 +401,7 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
|||||||
<TextField
|
<TextField
|
||||||
label="Field Label"
|
label="Field Label"
|
||||||
value={field.label || ''}
|
value={field.label || ''}
|
||||||
onChange={() => { }}
|
onChange={(e) => handleTextLabelChange(field.id, e.target.value, step.id, key)}
|
||||||
fullWidth
|
fullWidth
|
||||||
margin="normal"
|
margin="normal"
|
||||||
InputProps={{
|
InputProps={{
|
||||||
@@ -394,6 +426,23 @@ export const RightSidePanel: React.FC<RightSidePanelProps> = ({ onFinishCapture
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{!confirmedListTextFields[step.id]?.[key] && (
|
||||||
|
<Box display="flex" justifyContent="space-between" gap={2}>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => handleListTextFieldConfirm(step.id, key)}
|
||||||
|
disabled={!field.label?.trim()}
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => handleListTextFieldDiscard(step.id, key)}
|
||||||
|
>
|
||||||
|
Discard
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -42,12 +42,15 @@ interface BrowserStepsContextType {
|
|||||||
addScreenshotStep: (fullPage: boolean) => void;
|
addScreenshotStep: (fullPage: boolean) => void;
|
||||||
deleteBrowserStep: (id: number) => void;
|
deleteBrowserStep: (id: number) => void;
|
||||||
updateBrowserTextStepLabel: (id: number, newLabel: string) => void;
|
updateBrowserTextStepLabel: (id: number, newLabel: string) => void;
|
||||||
|
updateListTextFieldLabel: (listId: number, fieldKey: string, newLabel: string) => void;
|
||||||
|
removeListTextField: (listId: number, fieldKey: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BrowserStepsContext = createContext<BrowserStepsContextType | undefined>(undefined);
|
const BrowserStepsContext = createContext<BrowserStepsContextType | undefined>(undefined);
|
||||||
|
|
||||||
export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const [browserSteps, setBrowserSteps] = useState<BrowserStep[]>([]);
|
const [browserSteps, setBrowserSteps] = useState<BrowserStep[]>([]);
|
||||||
|
const [discardedFields, setDiscardedFields] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
const addTextStep = (label: string, data: string, selectorObj: SelectorObject) => {
|
const addTextStep = (label: string, data: string, selectorObj: SelectorObject) => {
|
||||||
setBrowserSteps(prevSteps => [
|
setBrowserSteps(prevSteps => [
|
||||||
@@ -62,12 +65,19 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
step => step.type === 'list' && step.id === listId
|
step => step.type === 'list' && step.id === listId
|
||||||
);
|
);
|
||||||
if (existingListStepIndex !== -1) {
|
if (existingListStepIndex !== -1) {
|
||||||
// Update the existing ListStep with new fields
|
// Update the existing ListStep with new fields, excluding discarded ones
|
||||||
const updatedSteps = [...prevSteps];
|
const updatedSteps = [...prevSteps];
|
||||||
const existingListStep = updatedSteps[existingListStepIndex] as ListStep;
|
const existingListStep = updatedSteps[existingListStepIndex] as ListStep;
|
||||||
|
const filteredNewFields = Object.entries(newFields).reduce((acc, [key, value]) => {
|
||||||
|
if (!discardedFields.has(`${listId}-${key}`)) {
|
||||||
|
acc[key] = value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {} as { [key: string]: TextStep });
|
||||||
|
|
||||||
updatedSteps[existingListStepIndex] = {
|
updatedSteps[existingListStepIndex] = {
|
||||||
...existingListStep,
|
...existingListStep,
|
||||||
fields: { ...existingListStep.fields, ...newFields },
|
fields: { ...existingListStep.fields, ...filteredNewFields },
|
||||||
pagination: pagination,
|
pagination: pagination,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
};
|
};
|
||||||
@@ -81,7 +91,6 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addScreenshotStep = (fullPage: boolean) => {
|
const addScreenshotStep = (fullPage: boolean) => {
|
||||||
setBrowserSteps(prevSteps => [
|
setBrowserSteps(prevSteps => [
|
||||||
...prevSteps,
|
...prevSteps,
|
||||||
@@ -101,6 +110,41 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateListTextFieldLabel = (listId: number, fieldKey: string, newLabel: string) => {
|
||||||
|
setBrowserSteps(prevSteps =>
|
||||||
|
prevSteps.map(step => {
|
||||||
|
if (step.type === 'list' && step.id === listId) {
|
||||||
|
return {
|
||||||
|
...step,
|
||||||
|
fields: {
|
||||||
|
...step.fields,
|
||||||
|
[fieldKey]: {
|
||||||
|
...step.fields[fieldKey],
|
||||||
|
label: newLabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return step;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeListTextField = (listId: number, fieldKey: string) => {
|
||||||
|
setBrowserSteps(prevSteps =>
|
||||||
|
prevSteps.map(step => {
|
||||||
|
if (step.type === 'list' && step.id === listId) {
|
||||||
|
const { [fieldKey]: _, ...remainingFields } = step.fields;
|
||||||
|
return {
|
||||||
|
...step,
|
||||||
|
fields: remainingFields
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return step;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setDiscardedFields(prevDiscarded => new Set(prevDiscarded).add(`${listId}-${fieldKey}`));
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<BrowserStepsContext.Provider value={{
|
<BrowserStepsContext.Provider value={{
|
||||||
browserSteps,
|
browserSteps,
|
||||||
@@ -109,6 +153,8 @@ export const BrowserStepsProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
addScreenshotStep,
|
addScreenshotStep,
|
||||||
deleteBrowserStep,
|
deleteBrowserStep,
|
||||||
updateBrowserTextStepLabel,
|
updateBrowserTextStepLabel,
|
||||||
|
updateListTextFieldLabel,
|
||||||
|
removeListTextField,
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</BrowserStepsContext.Provider>
|
</BrowserStepsContext.Provider>
|
||||||
|
|||||||
Reference in New Issue
Block a user