Snai3i-LandingPage-FormBuilder / frontend / src / app / slices / formPlayground.ts
formPlayground.ts
Raw
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { nanoid } from "@reduxjs/toolkit";
import { arrayMove } from "@dnd-kit/sortable";
import { formsApi } from "../backend/endpoints/forms";

interface FormPlaygroundState {
  formElements: FormElementsType[];
}

const initialState: FormPlaygroundState = {
  formElements: [],
};

const formPlaygroundSlice = createSlice({
  name: "formPlayground",
  initialState,
  reducers: {
    addFormElement(
      state,
      action: PayloadAction<{ label: string; type: string }>
    ) {
      const { label, type } = action.payload;
      state.formElements.push({
        id: nanoid(),
        label,
        type,
        required: false,
        options: ["checklist", "multi-choice", "dropdown", "combobox"].includes(
          type
        )
          ? [
              { label: "Option 1", value: nanoid() },
              { label: "Option 2", value: nanoid() },
            ]
          : undefined,
      });
    },
    moveFormElement(
      state,
      action: PayloadAction<{ oldIndex: number; newIndex: number }>
    ) {
      const { oldIndex, newIndex } = action.payload;
      state.formElements = arrayMove(state.formElements, oldIndex, newIndex);
    },
    updateLabel(state, action: PayloadAction<{ id: string; label: string }>) {
      const { id, label } = action.payload;
      const formElement = state.formElements.find((el) => el.id === id);
      if (formElement) {
        formElement.label = label;
      }
    },
    toggleRequired(state, action: PayloadAction<string>) {
      const id = action.payload;
      const formElement = state.formElements.find((el) => el.id === id);

      if (formElement) {
        formElement.required = !formElement.required;
      }
    },
    addOption(state, action: PayloadAction<string>) {
      const id = action.payload;
      const formElement = state.formElements.find((el) => el.id === id);
      if (formElement) {
        const optionId = nanoid();
        formElement.options?.push({
          label: "Option " + (formElement.options.length + 1),
          value: optionId,
        });
      }
    },
    updateOption(
      state,
      action: PayloadAction<{ id: string; optionId: string; label: string }>
    ) {
      const { id, optionId, label } = action.payload;
      const formElement = state.formElements.find((el) => el.id === id);
      if (formElement) {
        const option = formElement.options?.find(
          (opt) => opt.value === optionId
        );
        if (option) {
          option.label = label;
        }
      }
    },
    deleteOption(
      state,
      action: PayloadAction<{ id: string; optionId: string }>
    ) {
      const { id, optionId } = action.payload;
      const formElement = state.formElements.find((el) => el.id === id);
      if (formElement && formElement.options) {
        formElement.options = formElement.options.filter(
          (opt) => opt.value !== optionId
        );
      }
    },
    removeFormElement(state, action: PayloadAction<string>) {
      const id = action.payload;
      state.formElements = state.formElements.filter((el) => el.id !== id);
    },
    removeAllFormElements(state) {
      state.formElements = [];
    },
    setFormElements(state, action: PayloadAction<FormElementsType[]>) {
      state.formElements = action.payload;
    },
  },
  extraReducers: (builder) => {
    builder.addMatcher(
      formsApi.endpoints.getForm.matchFulfilled,
      (state, action) => {
        state.formElements = action.payload.data.elements;
      }
    );
  },
});

export const {
  addFormElement,
  moveFormElement,
  updateLabel,
  toggleRequired,
  addOption,
  updateOption,
  deleteOption,
  removeFormElement,
  removeAllFormElements,
  setFormElements,
} = formPlaygroundSlice.actions;

export default formPlaygroundSlice.reducer;