mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-06 09:00:18 -06:00
feat: adds response status select in filters (#6325)
This commit is contained in:
@@ -28,7 +28,7 @@ const TestComponent = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="onlyComplete">{selectedFilter.onlyComplete.toString()}</div>
|
||||
<div data-testid="responseStatus">{selectedFilter.responseStatus}</div>
|
||||
<div data-testid="filterLength">{selectedFilter.filter.length}</div>
|
||||
<div data-testid="questionOptionsLength">{selectedOptions.questionOptions.length}</div>
|
||||
<div data-testid="questionFilterOptionsLength">{selectedOptions.questionFilterOptions.length}</div>
|
||||
@@ -44,7 +44,7 @@ const TestComponent = () => {
|
||||
filterType: { filterValue: "value1", filterComboBoxValue: "option1" },
|
||||
},
|
||||
],
|
||||
onlyComplete: true,
|
||||
responseStatus: "complete",
|
||||
})
|
||||
}>
|
||||
Update Filter
|
||||
@@ -81,7 +81,7 @@ describe("ResponseFilterContext", () => {
|
||||
</ResponseFilterProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("onlyComplete").textContent).toBe("false");
|
||||
expect(screen.getByTestId("responseStatus").textContent).toBe("all");
|
||||
expect(screen.getByTestId("filterLength").textContent).toBe("0");
|
||||
expect(screen.getByTestId("questionOptionsLength").textContent).toBe("0");
|
||||
expect(screen.getByTestId("questionFilterOptionsLength").textContent).toBe("0");
|
||||
@@ -99,7 +99,7 @@ describe("ResponseFilterContext", () => {
|
||||
const updateButton = screen.getByText("Update Filter");
|
||||
await userEvent.click(updateButton);
|
||||
|
||||
expect(screen.getByTestId("onlyComplete").textContent).toBe("true");
|
||||
expect(screen.getByTestId("responseStatus").textContent).toBe("complete");
|
||||
expect(screen.getByTestId("filterLength").textContent).toBe("1");
|
||||
});
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@ export interface FilterValue {
|
||||
};
|
||||
}
|
||||
|
||||
export type TResponseStatus = "all" | "complete" | "partial";
|
||||
|
||||
export interface SelectedFilterValue {
|
||||
filter: FilterValue[];
|
||||
onlyComplete: boolean;
|
||||
responseStatus: TResponseStatus;
|
||||
}
|
||||
|
||||
interface SelectedFilterOptions {
|
||||
@@ -47,7 +49,7 @@ const ResponseFilterProvider = ({ children }: { children: React.ReactNode }) =>
|
||||
// state holds the filter selected value
|
||||
const [selectedFilter, setSelectedFilter] = useState<SelectedFilterValue>({
|
||||
filter: [],
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
});
|
||||
// state holds all the options of the responses fetched
|
||||
const [selectedOptions, setSelectedOptions] = useState<SelectedFilterOptions>({
|
||||
@@ -67,7 +69,7 @@ const ResponseFilterProvider = ({ children }: { children: React.ReactNode }) =>
|
||||
});
|
||||
setSelectedFilter({
|
||||
filter: [],
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ const mockSurvey = {
|
||||
variables: [],
|
||||
} as unknown as TSurvey;
|
||||
|
||||
const mockSelectedFilter = { filter: [], onlyComplete: false };
|
||||
const mockSelectedFilter = { filter: [], responseStatus: "all" };
|
||||
const mockSetSelectedFilter = vi.fn();
|
||||
|
||||
const defaultProps = {
|
||||
@@ -309,17 +309,13 @@ describe("SummaryList", () => {
|
||||
|
||||
test("renders EmptySpaceFiller when responseCount is 0 and summary is not empty (no responses match filter)", () => {
|
||||
const summaryWithItem = [createMockQuestionSummary("q1", TSurveyQuestionTypeEnum.OpenText)];
|
||||
render(
|
||||
<SummaryList {...defaultProps} summary={summaryWithItem} responseCount={0} totalResponseCount={10} />
|
||||
);
|
||||
render(<SummaryList {...defaultProps} summary={summaryWithItem} responseCount={0} />);
|
||||
expect(screen.getByText("Mocked EmptySpaceFiller")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders EmptySpaceFiller when responseCount is 0 and totalResponseCount is 0 (no responses at all)", () => {
|
||||
const summaryWithItem = [createMockQuestionSummary("q1", TSurveyQuestionTypeEnum.OpenText)];
|
||||
render(
|
||||
<SummaryList {...defaultProps} summary={summaryWithItem} responseCount={0} totalResponseCount={0} />
|
||||
);
|
||||
render(<SummaryList {...defaultProps} summary={summaryWithItem} responseCount={0} />);
|
||||
expect(screen.getByText("Mocked EmptySpaceFiller")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -397,7 +393,7 @@ describe("SummaryList", () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
});
|
||||
// Ensure vi.mocked(toast.success) refers to the spy from the named export
|
||||
expect(vi.mocked(toast).success).toHaveBeenCalledWith("Custom add message", { duration: 5000 });
|
||||
@@ -425,7 +421,7 @@ describe("SummaryList", () => {
|
||||
},
|
||||
};
|
||||
vi.mocked(useResponseFilter).mockReturnValue({
|
||||
selectedFilter: { filter: [existingFilter], onlyComplete: false },
|
||||
selectedFilter: { filter: [existingFilter], responseStatus: "all" },
|
||||
setSelectedFilter: mockSetSelectedFilter,
|
||||
resetFilter: vi.fn(),
|
||||
} as any);
|
||||
@@ -454,7 +450,7 @@ describe("SummaryList", () => {
|
||||
},
|
||||
},
|
||||
],
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
});
|
||||
expect(vi.mocked(toast.success)).toHaveBeenCalledWith(
|
||||
"environments.surveys.summary.filter_updated_successfully",
|
||||
|
||||
@@ -92,7 +92,7 @@ export const SummaryList = ({ summary, environment, responseCount, survey, local
|
||||
|
||||
setSelectedFilter({
|
||||
filter: [...filterObject.filter],
|
||||
onlyComplete: filterObject.onlyComplete,
|
||||
responseStatus: filterObject.responseStatus,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ export const QuestionFilterComboBox = ({
|
||||
</div>
|
||||
<div className="relative mt-2 h-full">
|
||||
{open && (
|
||||
<div className="animate-in bg-popover absolute top-0 z-10 max-h-52 w-full overflow-auto rounded-md bg-white outline-none">
|
||||
<div className="animate-in absolute top-0 z-10 max-h-52 w-full overflow-auto rounded-md bg-white outline-none">
|
||||
<CommandList>
|
||||
<div className="p-2">
|
||||
<Input
|
||||
|
||||
@@ -188,7 +188,7 @@ export const QuestionsComboBox = ({ options, selected, onChangeValue }: Question
|
||||
</button>
|
||||
<div className="relative mt-2 h-full">
|
||||
{open && (
|
||||
<div className="animate-in bg-popover absolute top-0 z-50 max-h-52 w-full overflow-auto rounded-md bg-white outline-none">
|
||||
<div className="animate-in absolute top-0 z-50 w-full overflow-auto rounded-md bg-white outline-none">
|
||||
<CommandList>
|
||||
<CommandEmpty>{t("common.no_result_found")}</CommandEmpty>
|
||||
{options?.map((data) => (
|
||||
|
||||
@@ -30,6 +30,45 @@ vi.mock("@formkit/auto-animate/react", () => ({
|
||||
useAutoAnimate: () => [[vi.fn()]],
|
||||
}));
|
||||
|
||||
// Mock the Select components
|
||||
const mockOnValueChange = vi.fn();
|
||||
vi.mock("@/modules/ui/components/select", () => ({
|
||||
Select: ({ children, onValueChange, defaultValue }) => {
|
||||
// Store the onValueChange callback for testing
|
||||
mockOnValueChange.mockImplementation(onValueChange);
|
||||
return (
|
||||
<div data-testid="select-root" data-default-value={defaultValue}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
SelectTrigger: ({ children, className }) => (
|
||||
<div
|
||||
role="combobox"
|
||||
className={className}
|
||||
data-testid="select-trigger"
|
||||
tabIndex={0}
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox">
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
SelectValue: () => <span>environments.surveys.filter.complete_and_partial_responses</span>,
|
||||
SelectContent: ({ children }) => <div data-testid="select-content">{children}</div>,
|
||||
SelectItem: ({ value, children, ...props }) => (
|
||||
<div
|
||||
data-testid={`select-item-${value}`}
|
||||
data-value={value}
|
||||
onClick={() => mockOnValueChange(value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && mockOnValueChange(value)}
|
||||
role="option"
|
||||
tabIndex={0}
|
||||
{...props}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("./QuestionsComboBox", () => ({
|
||||
QuestionsComboBox: ({ onChangeValue }) => (
|
||||
<div data-testid="questions-combo-box">
|
||||
@@ -67,7 +106,7 @@ describe("ResponseFilter", () => {
|
||||
|
||||
const mockSelectedFilter = {
|
||||
filter: [],
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
};
|
||||
|
||||
const mockSelectedOptions = {
|
||||
@@ -145,7 +184,7 @@ describe("ResponseFilter", () => {
|
||||
expect(
|
||||
screen.getByText("environments.surveys.summary.show_all_responses_that_match")
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText("environments.surveys.summary.only_completed")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("select-trigger")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("fetches filter data when opened", async () => {
|
||||
@@ -160,7 +199,7 @@ describe("ResponseFilter", () => {
|
||||
test("handles adding new filter", async () => {
|
||||
// Start with an empty filter
|
||||
vi.mocked(useResponseFilter).mockReturnValue({
|
||||
selectedFilter: { filter: [], onlyComplete: false },
|
||||
selectedFilter: { filter: [], responseStatus: "all" },
|
||||
setSelectedFilter: mockSetSelectedFilter,
|
||||
selectedOptions: mockSelectedOptions,
|
||||
setSelectedOptions: mockSetSelectedOptions,
|
||||
@@ -178,14 +217,38 @@ describe("ResponseFilter", () => {
|
||||
expect(screen.getByTestId("questions-combo-box")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("handles only complete checkbox toggle", async () => {
|
||||
test("handles response status filter change to complete", async () => {
|
||||
render(<ResponseFilter survey={mockSurvey} />);
|
||||
|
||||
await userEvent.click(screen.getByText("Filter"));
|
||||
await userEvent.click(screen.getByRole("checkbox"));
|
||||
|
||||
// Simulate selecting "complete" by calling the mock function
|
||||
mockOnValueChange("complete");
|
||||
|
||||
await userEvent.click(screen.getByText("common.apply_filters"));
|
||||
|
||||
expect(mockSetSelectedFilter).toHaveBeenCalledWith({ filter: [], onlyComplete: true });
|
||||
expect(mockSetSelectedFilter).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
responseStatus: "complete",
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("handles response status filter change to partial", async () => {
|
||||
render(<ResponseFilter survey={mockSurvey} />);
|
||||
|
||||
await userEvent.click(screen.getByText("Filter"));
|
||||
|
||||
// Simulate selecting "partial" by calling the mock function
|
||||
mockOnValueChange("partial");
|
||||
|
||||
await userEvent.click(screen.getByText("common.apply_filters"));
|
||||
|
||||
expect(mockSetSelectedFilter).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
responseStatus: "partial",
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("handles selecting question and filter options", async () => {
|
||||
@@ -199,7 +262,7 @@ describe("ResponseFilter", () => {
|
||||
filterType: { filterComboBoxValue: undefined, filterValue: undefined },
|
||||
},
|
||||
],
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
},
|
||||
setSelectedFilter: setSelectedFilterMock,
|
||||
selectedOptions: mockSelectedOptions,
|
||||
@@ -228,6 +291,6 @@ describe("ResponseFilter", () => {
|
||||
await userEvent.click(screen.getByText("Filter"));
|
||||
await userEvent.click(screen.getByText("common.clear_all"));
|
||||
|
||||
expect(mockSetSelectedFilter).toHaveBeenCalledWith({ filter: [], onlyComplete: false });
|
||||
expect(mockSetSelectedFilter).toHaveBeenCalledWith({ filter: [], responseStatus: "all" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,17 +2,23 @@
|
||||
|
||||
import {
|
||||
SelectedFilterValue,
|
||||
TResponseStatus,
|
||||
useResponseFilter,
|
||||
} from "@/app/(app)/environments/[environmentId]/components/ResponseFilterContext";
|
||||
import { getSurveyFilterDataAction } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/actions";
|
||||
import { QuestionFilterComboBox } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/components/QuestionFilterComboBox";
|
||||
import { generateQuestionAndFilterOptions } from "@/app/lib/surveys/surveys";
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { Checkbox } from "@/modules/ui/components/checkbox";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/modules/ui/components/popover";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/modules/ui/components/select";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { useTranslate } from "@tolgee/react";
|
||||
import clsx from "clsx";
|
||||
import { ChevronDown, ChevronUp, Plus, TrashIcon } from "lucide-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { TSurvey, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
|
||||
@@ -72,7 +78,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
)?.filterOptions[0],
|
||||
},
|
||||
};
|
||||
setFilterValue({ filter: [...filterValue.filter], onlyComplete: filterValue.onlyComplete });
|
||||
setFilterValue({ filter: [...filterValue.filter], responseStatus: filterValue.responseStatus });
|
||||
} else {
|
||||
// Update the existing value at the specified index
|
||||
filterValue.filter[index].questionType = value;
|
||||
@@ -93,7 +99,7 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
// keep the filter if questionType is selected and filterComboBoxValue is selected
|
||||
return s.questionType.hasOwnProperty("label") && s.filterType.filterComboBoxValue?.length;
|
||||
}),
|
||||
onlyComplete: filterValue.onlyComplete,
|
||||
responseStatus: filterValue.responseStatus,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -120,8 +126,8 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
};
|
||||
|
||||
const handleClearAllFilters = () => {
|
||||
setFilterValue((filterValue) => ({ ...filterValue, filter: [] }));
|
||||
setSelectedFilter((selectedFilters) => ({ ...selectedFilters, filter: [] }));
|
||||
setFilterValue((filterValue) => ({ ...filterValue, filter: [], responseStatus: "all" }));
|
||||
setSelectedFilter((selectedFilters) => ({ ...selectedFilters, filter: [], responseStatus: "all" }));
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
@@ -158,8 +164,8 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
setFilterValue({ ...filterValue });
|
||||
};
|
||||
|
||||
const handleCheckOnlyComplete = (checked: boolean) => {
|
||||
setFilterValue({ ...filterValue, onlyComplete: checked });
|
||||
const handleResponseStatusChange = (responseStatus: TResponseStatus) => {
|
||||
setFilterValue({ ...filterValue, responseStatus });
|
||||
};
|
||||
|
||||
// remove the filter which has already been selected
|
||||
@@ -203,8 +209,9 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align="start"
|
||||
className="w-[300px] border-slate-200 bg-slate-100 p-6 sm:w-[400px] md:w-[750px] lg:w-[1000px]">
|
||||
<div className="mb-8 flex flex-wrap items-start justify-between">
|
||||
className="w-[300px] border-slate-200 bg-slate-100 p-6 sm:w-[400px] md:w-[750px] lg:w-[1000px]"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}>
|
||||
<div className="mb-8 flex flex-wrap items-start justify-between gap-2">
|
||||
<p className="text-slate800 hidden text-lg font-semibold sm:block">
|
||||
{t("environments.surveys.summary.show_all_responses_that_match")}
|
||||
</p>
|
||||
@@ -212,16 +219,24 @@ export const ResponseFilter = ({ survey }: ResponseFilterProps) => {
|
||||
{t("environments.surveys.summary.show_all_responses_where")}
|
||||
</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<label className="text-sm font-normal text-slate-600">
|
||||
{t("environments.surveys.summary.only_completed")}
|
||||
</label>
|
||||
<Checkbox
|
||||
className={clsx("rounded-md", filterValue.onlyComplete && "bg-black text-white")}
|
||||
checked={filterValue.onlyComplete}
|
||||
onCheckedChange={(checked) => {
|
||||
typeof checked === "boolean" && handleCheckOnlyComplete(checked);
|
||||
<Select
|
||||
onValueChange={(val) => {
|
||||
handleResponseStatusChange(val as TResponseStatus);
|
||||
}}
|
||||
/>
|
||||
defaultValue={filterValue.responseStatus}>
|
||||
<SelectTrigger className="w-full bg-white">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent position="popper">
|
||||
<SelectItem value="all">
|
||||
{t("environments.surveys.filter.complete_and_partial_responses")}
|
||||
</SelectItem>
|
||||
<SelectItem value="complete">
|
||||
{t("environments.surveys.filter.complete_responses")}
|
||||
</SelectItem>
|
||||
<SelectItem value="partial">{t("environments.surveys.filter.partial_responses")}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -320,7 +320,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should return empty filters when no selections", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [],
|
||||
};
|
||||
|
||||
@@ -331,7 +331,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by completed responses", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: true,
|
||||
responseStatus: "complete",
|
||||
filter: [],
|
||||
};
|
||||
|
||||
@@ -342,7 +342,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by date range", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [],
|
||||
};
|
||||
|
||||
@@ -355,7 +355,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by tags", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: { type: "Tags", label: "Tag 1", id: "tag1" },
|
||||
@@ -376,7 +376,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by open text questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -397,7 +397,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by address questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -418,7 +418,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by contact info questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -439,7 +439,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by ranking questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -460,7 +460,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by multiple choice single questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -481,7 +481,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by multiple choice multi questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -502,7 +502,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by NPS questions with different operations", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -523,7 +523,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by rating questions with less than operation", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -544,7 +544,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by CTA questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -565,7 +565,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by consent questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -586,7 +586,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by picture selection questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -607,7 +607,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by matrix questions", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
@@ -628,7 +628,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by hidden fields", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: { type: "Hidden Fields", label: "plan", id: "plan" },
|
||||
@@ -644,7 +644,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by attributes", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: { type: "Attributes", label: "role", id: "role" },
|
||||
@@ -660,7 +660,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by other filters", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: { type: "Other Filters", label: "Language", id: "language" },
|
||||
@@ -676,7 +676,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should filter by meta fields", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: false,
|
||||
responseStatus: "all",
|
||||
filter: [
|
||||
{
|
||||
questionType: { type: "Meta", label: "source", id: "source" },
|
||||
@@ -692,7 +692,7 @@ describe("surveys", () => {
|
||||
|
||||
test("should handle multiple filters together", () => {
|
||||
const selectedFilter: SelectedFilterValue = {
|
||||
onlyComplete: true,
|
||||
responseStatus: "complete",
|
||||
filter: [
|
||||
{
|
||||
questionType: {
|
||||
|
||||
@@ -242,8 +242,10 @@ export const getFormattedFilters = (
|
||||
});
|
||||
|
||||
// for completed responses
|
||||
if (selectedFilter.onlyComplete) {
|
||||
if (selectedFilter.responseStatus === "complete") {
|
||||
filters["finished"] = true;
|
||||
} else if (selectedFilter.responseStatus === "partial") {
|
||||
filters["finished"] = false;
|
||||
}
|
||||
|
||||
// for date range responses
|
||||
|
||||
@@ -510,13 +510,11 @@
|
||||
"create_action": "Aktion erstellen",
|
||||
"css_selector": "CSS-Selektor",
|
||||
"delete_action_text": "Bist Du sicher, dass Du diese Aktion löschen möchtest? Dadurch wird diese Aktion auch als Auslöser aus all deinen Umfragen entfernt.",
|
||||
"display_name": "Anzeigename",
|
||||
"does_not_contain": "Enthält nicht",
|
||||
"does_not_exactly_match": "Stimmt nicht genau überein",
|
||||
"eg_clicked_download": "z.B. 'Herunterladen' geklickt",
|
||||
"eg_download_cta_click_on_home": "z.B. Download-CTA-Klick auf der Startseite",
|
||||
"eg_install_app": "z.B. App installieren",
|
||||
"eg_user_clicked_download_button": "z.B. Benutzer hat auf 'Herunterladen' geklickt",
|
||||
"ends_with": "endet mit",
|
||||
"enter_a_url_to_see_if_a_user_visiting_it_would_be_tracked": "Teste eine URL, um zu sehen, ob der Nutzer deine Umfrage sehen würde.",
|
||||
"enter_url": "z.B. https://app.com/dashboard",
|
||||
@@ -1616,6 +1614,11 @@
|
||||
"zip": "Postleitzahl"
|
||||
},
|
||||
"error_deleting_survey": "Beim Löschen der Umfrage ist ein Fehler aufgetreten",
|
||||
"filter": {
|
||||
"complete_and_partial_responses": "Vollständige und Teilantworten",
|
||||
"complete_responses": "Vollständige Antworten",
|
||||
"partial_responses": "Teilantworten"
|
||||
},
|
||||
"new_survey": "Neue Umfrage",
|
||||
"no_surveys_created_yet": "Noch keine Umfragen erstellt",
|
||||
"open_options": "Optionen öffnen",
|
||||
|
||||
@@ -510,13 +510,11 @@
|
||||
"create_action": "Create action",
|
||||
"css_selector": "CSS Selector",
|
||||
"delete_action_text": "Are you sure you want to delete this action? This also removes this action as a trigger from all your surveys.",
|
||||
"display_name": "Display name",
|
||||
"does_not_contain": "Does not contain",
|
||||
"does_not_exactly_match": "Does not exactly match",
|
||||
"eg_clicked_download": "E.g. Clicked Download",
|
||||
"eg_download_cta_click_on_home": "e.g. download_cta_click_on_home",
|
||||
"eg_install_app": "E.g. Install App",
|
||||
"eg_user_clicked_download_button": "E.g. User clicked Download Button",
|
||||
"ends_with": "Ends with",
|
||||
"enter_a_url_to_see_if_a_user_visiting_it_would_be_tracked": "Enter a URL to see if a user visiting it would be tracked.",
|
||||
"enter_url": "e.g. https://app.com/dashboard",
|
||||
@@ -1616,6 +1614,11 @@
|
||||
"zip": "Zip"
|
||||
},
|
||||
"error_deleting_survey": "An error occured while deleting survey",
|
||||
"filter": {
|
||||
"complete_and_partial_responses": "Complete and partial responses",
|
||||
"complete_responses": "Complete responses",
|
||||
"partial_responses": "Partial responses"
|
||||
},
|
||||
"new_survey": "New Survey",
|
||||
"no_surveys_created_yet": "No surveys created yet",
|
||||
"open_options": "Open options",
|
||||
|
||||
@@ -510,13 +510,11 @@
|
||||
"create_action": "Créer une action",
|
||||
"css_selector": "Sélecteur CSS",
|
||||
"delete_action_text": "Êtes-vous sûr de vouloir supprimer cette action ? Cela supprime également cette action en tant que déclencheur de toutes vos enquêtes.",
|
||||
"display_name": "Nom d'affichage",
|
||||
"does_not_contain": "Ne contient pas",
|
||||
"does_not_exactly_match": "Ne correspond pas exactement",
|
||||
"eg_clicked_download": "Par exemple, cliqué sur Télécharger",
|
||||
"eg_download_cta_click_on_home": "Par exemple, cliquez sur le CTA de téléchargement sur la page d'accueil",
|
||||
"eg_install_app": "Par exemple, installer l'application",
|
||||
"eg_user_clicked_download_button": "Par exemple, l'utilisateur a cliqué sur le bouton de téléchargement.",
|
||||
"ends_with": "Se termine par",
|
||||
"enter_a_url_to_see_if_a_user_visiting_it_would_be_tracked": "Saisissez une URL pour voir si un utilisateur la visitant serait suivi.",
|
||||
"enter_url": "par exemple https://app.com/dashboard",
|
||||
@@ -1616,6 +1614,11 @@
|
||||
"zip": "Zip"
|
||||
},
|
||||
"error_deleting_survey": "Une erreur est survenue lors de la suppression de l'enquête.",
|
||||
"filter": {
|
||||
"complete_and_partial_responses": "Réponses complètes et partielles",
|
||||
"complete_responses": "Réponses complètes",
|
||||
"partial_responses": "Réponses partielles"
|
||||
},
|
||||
"new_survey": "Nouveau Sondage",
|
||||
"no_surveys_created_yet": "Aucun sondage créé pour le moment",
|
||||
"open_options": "Ouvrir les options",
|
||||
|
||||
@@ -510,13 +510,11 @@
|
||||
"create_action": "criar ação",
|
||||
"css_selector": "Seletor CSS",
|
||||
"delete_action_text": "Tem certeza de que quer deletar essa ação? Isso também vai remover essa ação como gatilho de todas as suas pesquisas.",
|
||||
"display_name": "Nome de exibição",
|
||||
"does_not_contain": "não contém",
|
||||
"does_not_exactly_match": "Não bate exatamente",
|
||||
"eg_clicked_download": "Por exemplo, clicou em baixar",
|
||||
"eg_download_cta_click_on_home": "e.g. download_cta_click_on_home",
|
||||
"eg_install_app": "Ex: Instalar App",
|
||||
"eg_user_clicked_download_button": "Por exemplo, usuário clicou no botão de download",
|
||||
"ends_with": "Termina com",
|
||||
"enter_a_url_to_see_if_a_user_visiting_it_would_be_tracked": "Digite uma URL para ver se um usuário que a visita seria rastreado.",
|
||||
"enter_url": "ex.: https://app.com/dashboard",
|
||||
@@ -1616,6 +1614,11 @@
|
||||
"zip": "Fecho éclair"
|
||||
},
|
||||
"error_deleting_survey": "Ocorreu um erro ao deletar a pesquisa",
|
||||
"filter": {
|
||||
"complete_and_partial_responses": "Respostas completas e parciais",
|
||||
"complete_responses": "Respostas completas",
|
||||
"partial_responses": "Respostas parciais"
|
||||
},
|
||||
"new_survey": "Nova Pesquisa",
|
||||
"no_surveys_created_yet": "Ainda não foram criadas pesquisas",
|
||||
"open_options": "Abre opções",
|
||||
|
||||
@@ -510,13 +510,11 @@
|
||||
"create_action": "Criar ação",
|
||||
"css_selector": "Seletor CSS",
|
||||
"delete_action_text": "Tem a certeza de que deseja eliminar esta ação? Isto também remove esta ação como um gatilho de todos os seus inquéritos.",
|
||||
"display_name": "Nome de exibição",
|
||||
"does_not_contain": "Não contém",
|
||||
"does_not_exactly_match": "Não corresponde exatamente",
|
||||
"eg_clicked_download": "Por exemplo, Clicou em Descarregar",
|
||||
"eg_download_cta_click_on_home": "por exemplo, descarregar_cta_clicar_em_home",
|
||||
"eg_install_app": "Ex. Instalar App",
|
||||
"eg_user_clicked_download_button": "Por exemplo, Utilizador clicou no Botão Descarregar",
|
||||
"ends_with": "Termina com",
|
||||
"enter_a_url_to_see_if_a_user_visiting_it_would_be_tracked": "Introduza um URL para ver se um utilizador que o visita seria rastreado.",
|
||||
"enter_url": "por exemplo, https://app.com/dashboard",
|
||||
@@ -1616,6 +1614,11 @@
|
||||
"zip": "Comprimir"
|
||||
},
|
||||
"error_deleting_survey": "Ocorreu um erro ao eliminar o questionário",
|
||||
"filter": {
|
||||
"complete_and_partial_responses": "Respostas completas e parciais",
|
||||
"complete_responses": "Respostas completas",
|
||||
"partial_responses": "Respostas parciais"
|
||||
},
|
||||
"new_survey": "Novo inquérito",
|
||||
"no_surveys_created_yet": "Ainda não foram criados questionários",
|
||||
"open_options": "Abrir opções",
|
||||
|
||||
@@ -510,13 +510,11 @@
|
||||
"create_action": "建立操作",
|
||||
"css_selector": "CSS 選取器",
|
||||
"delete_action_text": "您確定要刪除此操作嗎?這也會從您的所有問卷中移除此操作作為觸發器。",
|
||||
"display_name": "顯示名稱",
|
||||
"does_not_contain": "不包含",
|
||||
"does_not_exactly_match": "不完全相符",
|
||||
"eg_clicked_download": "例如,點擊下載",
|
||||
"eg_download_cta_click_on_home": "例如,download_cta_click_on_home",
|
||||
"eg_install_app": "例如,安裝應用程式",
|
||||
"eg_user_clicked_download_button": "例如,使用者點擊了下載按鈕",
|
||||
"ends_with": "結尾為",
|
||||
"enter_a_url_to_see_if_a_user_visiting_it_would_be_tracked": "輸入網址以查看造訪該網址的使用者是否會被追蹤。",
|
||||
"enter_url": "例如 https://app.com/dashboard",
|
||||
@@ -1616,6 +1614,11 @@
|
||||
"zip": "郵遞區號"
|
||||
},
|
||||
"error_deleting_survey": "刪除問卷時發生錯誤",
|
||||
"filter": {
|
||||
"complete_and_partial_responses": "完整 和 部分 回應",
|
||||
"complete_responses": "完整回應",
|
||||
"partial_responses": "部分回應"
|
||||
},
|
||||
"new_survey": "新增問卷",
|
||||
"no_surveys_created_yet": "尚未建立任何問卷",
|
||||
"open_options": "開啟選項",
|
||||
|
||||
@@ -60,7 +60,7 @@ function CommandInput({
|
||||
...props
|
||||
}: React.ComponentProps<typeof CommandPrimitive.Input> & { hidden?: boolean }) {
|
||||
return (
|
||||
<div data-slot="command-input-wrapper" className={cn("flex h-11 items-center")}>
|
||||
<div data-slot="command-input-wrapper" className={cn("flex items-center")}>
|
||||
<SearchIcon className="h-4 w-4 shrink-0 text-slate-500" />
|
||||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
|
||||
Reference in New Issue
Block a user