mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-21 19:39:28 -05:00
Merge branch 'main' of github.com:formbricks/formbricks into feature/FOR-772
This commit is contained in:
+2
-1
@@ -31,6 +31,7 @@ COPY --from=installer /app/apps/web/package.json .
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/packages/database/prisma ./packages/database/prisma
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/packages/database/schema.prisma ./packages/database/schema.prisma
|
||||
COPY --from=installer --chown=nextjs:nodejs /app/packages/database/migrations ./packages/database/migrations
|
||||
|
||||
CMD pnpm dlx prisma migrate deploy && node apps/web/server.js
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { useAttributeClasses } from "@/lib/attributeClasses/attributeClasses";
|
||||
import { Button, ErrorComponent } from "@formbricks/ui";
|
||||
import { Badge, Button, ErrorComponent, Switch } from "@formbricks/ui";
|
||||
import { QuestionMarkCircleIcon, TagIcon } from "@heroicons/react/24/solid";
|
||||
import { useState } from "react";
|
||||
import AttributeDetailModal from "./AttributeDetailModal";
|
||||
@@ -16,6 +16,7 @@ export default function AttributeClassesList({ environmentId }: { environmentId:
|
||||
const [isAttributeDetailModalOpen, setAttributeDetailModalOpen] = useState(false);
|
||||
const [isUploadCSVModalOpen, setUploadCSVModalOpen] = useState(false);
|
||||
const [activeAttributeClass, setActiveAttributeClass] = useState("" as any);
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
|
||||
if (isLoadingAttributeClasses) {
|
||||
return <LoadingSpinner />;
|
||||
@@ -30,9 +31,24 @@ export default function AttributeClassesList({ environmentId }: { environmentId:
|
||||
setAttributeDetailModalOpen(true);
|
||||
};
|
||||
|
||||
const toggleShowArchived = () => {
|
||||
setShowArchived(!showArchived);
|
||||
};
|
||||
|
||||
const displayedAttributeClasses = showArchived ? attributeClasses : attributeClasses.filter(ac => !ac.archived);
|
||||
const hasArchived = attributeClasses.some(ac => ac.archived);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-6 text-right">
|
||||
<div className="mb-6 text-right flex items-center justify-end">
|
||||
{hasArchived && <div className="flex items-center text-sm font-medium">
|
||||
Show archived
|
||||
<Switch
|
||||
className="mx-3"
|
||||
checked={showArchived}
|
||||
onCheckedChange={toggleShowArchived}
|
||||
/>
|
||||
</div>}
|
||||
<Button
|
||||
variant="secondary"
|
||||
href="http://formbricks.com/docs/attributes/custom-attributes"
|
||||
@@ -48,7 +64,7 @@ export default function AttributeClassesList({ environmentId }: { environmentId:
|
||||
<div className="text-center">Last Updated</div>
|
||||
</div>
|
||||
<div className="grid-cols-7">
|
||||
{attributeClasses.map((attributeClass) => (
|
||||
{displayedAttributeClasses.map((attributeClass) => (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
handleOpenAttributeDetailModalClick(e, attributeClass);
|
||||
@@ -62,7 +78,11 @@ export default function AttributeClassesList({ environmentId }: { environmentId:
|
||||
<TagIcon className="h-8 w-8 flex-shrink-0 text-slate-500" />
|
||||
</div>
|
||||
<div className="ml-4 text-left">
|
||||
<div className="font-medium text-slate-900">{attributeClass.name}</div>
|
||||
<div className="font-medium text-slate-900">{attributeClass.name}
|
||||
<span className="ml-2">
|
||||
{attributeClass.archived && <Badge text="Archived" type="gray" size="tiny" />}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-xs text-slate-400">{attributeClass.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useAttributeClassMutation } from "@/lib/attributeClasses/mutateAttribut
|
||||
import { Button, Input, Label } from "@formbricks/ui";
|
||||
import type { AttributeClass } from "@prisma/client";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { ArchiveBoxArrowDownIcon, ArchiveBoxXMarkIcon } from "@heroicons/react/24/solid";
|
||||
|
||||
interface AttributeSettingsTabProps {
|
||||
environmentId: string;
|
||||
@@ -31,6 +32,12 @@ export default function AttributeSettingsTab({
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleArchiveToggle = async () => {
|
||||
const data = { archived: !attributeClass.archived };
|
||||
await triggerAttributeClassMutate(data);
|
||||
mutateAttributeClasses();
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form className="space-y-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
@@ -67,13 +74,22 @@ export default function AttributeSettingsTab({
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex justify-between border-t border-slate-200 pt-6">
|
||||
<div>
|
||||
<div className="flex items-center">
|
||||
<Button
|
||||
variant="secondary"
|
||||
href="https://formbricks.com/docs/getting-started/identify-users"
|
||||
target="_blank">
|
||||
Read Docs
|
||||
</Button>
|
||||
{attributeClass.type !== "automatic" && (
|
||||
<Button className="ml-3" variant="secondary" onClick={handleArchiveToggle}>
|
||||
{attributeClass.archived ? (
|
||||
<> <ArchiveBoxXMarkIcon className="h-4 mr-2 text-slate-600" /><span>Unarchive</span></>
|
||||
) : (
|
||||
<> <ArchiveBoxArrowDownIcon className="h-4 mr-2 text-slate-600" /><span>Archive</span></>
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{attributeClass.type !== "automatic" && (
|
||||
<div className="flex space-x-2">
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function EventClassesList({ environmentId }) {
|
||||
<>
|
||||
<div className="mb-6 text-right">
|
||||
<Button
|
||||
variant="primary"
|
||||
variant="darkCTA"
|
||||
onClick={() => {
|
||||
setAddEventModalOpen(true);
|
||||
}}>
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function PersonDetails({ environmentId, personId }: PersonDetails
|
||||
const otherAttributes = useMemo(
|
||||
() =>
|
||||
person?.attributes?.filter(
|
||||
(attribute) => attribute.attributeClass.name !== "email" && attribute.attributeClass.name !== "userId"
|
||||
(attribute) => attribute.attributeClass.name !== "email" && attribute.attributeClass.name !== "userId" && !attribute.attributeClass.archived
|
||||
) as any[],
|
||||
[person]
|
||||
);
|
||||
|
||||
@@ -158,7 +158,7 @@ export default function WhoToSendCard({ environmentId, localSurvey, setLocalSurv
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{attributeClasses.map((attributeClass) => (
|
||||
{attributeClasses.filter(attributeClass => !attributeClass.archived).map((attributeClass) => (
|
||||
<SelectItem value={attributeClass.id}>{attributeClass.name}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ export default function ResponseNote({
|
||||
!isOpen && responseNotes.length && "group/hint cursor-pointer bg-white hover:-right-3",
|
||||
!isOpen && !responseNotes.length && "cursor-pointer bg-slate-50",
|
||||
isOpen
|
||||
? "-right-5 top-0 h-full w-1/4 bg-white"
|
||||
? "-right-5 top-0 h-5/6 max-h-[600px] w-1/4 bg-white"
|
||||
: responseNotes.length
|
||||
? "right-0 top-[8.33%] h-5/6 max-h-[600px] w-1/12"
|
||||
: "right-[120px] top-[8.333%] h-5/6 max-h-[600px] w-1/12 group-hover:right-[0]"
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ export default function SingleResponse({ data, environmentId, surveyId }: OpenTe
|
||||
className="group flex items-center"
|
||||
href={`/environments/${environmentId}/people/${data.personId}`}>
|
||||
<PersonAvatar personId={data.personId} />
|
||||
<h3 className="ph-no-capture ml-4 pb-1 font-semibold text-slate-600 group-hover:underline">
|
||||
<h3 className="ph-no-capture ml-4 pb-1 font-semibold text-slate-600 hover:underline">
|
||||
{displayIdentifier}
|
||||
</h3>
|
||||
</Link>
|
||||
|
||||
@@ -73,6 +73,7 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
select: {
|
||||
name: true,
|
||||
description: true,
|
||||
archived: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "AttributeClass" ADD COLUMN "archived" BOOLEAN NOT NULL DEFAULT false;
|
||||
@@ -64,6 +64,7 @@ model AttributeClass {
|
||||
updatedAt DateTime @updatedAt @map(name: "updated_at")
|
||||
name String
|
||||
description String?
|
||||
archived Boolean @default(false)
|
||||
type AttributeType
|
||||
environment Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
|
||||
environmentId String
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const ZResponseData = z.record(z.union([z.string(), z.number()]));
|
||||
export const ZResponseData = z.record(z.union([z.string(), z.number(), z.array(z.string())]));
|
||||
|
||||
export type TResponseData = z.infer<typeof ZResponseData>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user