mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-30 18:30:32 -06:00
feat: product settings page styling UI and service
This commit is contained in:
@@ -1,18 +1,139 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { RotateCcwIcon } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { TProduct } from "@formbricks/types/product";
|
||||
import { Button } from "@formbricks/ui/Button";
|
||||
import { ColorPicker } from "@formbricks/ui/ColorPicker";
|
||||
import { Slider } from "@formbricks/ui/Slider";
|
||||
import CardArrangement from "@formbricks/ui/Styling/CardArrangement";
|
||||
import DarkModeColors from "@formbricks/ui/Styling/DarkModeColors";
|
||||
import { Switch } from "@formbricks/ui/Switch";
|
||||
|
||||
import { updateProductAction } from "../actions";
|
||||
|
||||
type UnifiedStylingProps = {
|
||||
product: TProduct;
|
||||
};
|
||||
|
||||
const colorDefaults = {
|
||||
brandColor: "#64748b",
|
||||
questionColor: "#2b2524",
|
||||
inputColor: "#efefef",
|
||||
inputBorderColor: "#c0c0c0",
|
||||
cardBackgroundColor: "#c0c0c0",
|
||||
highlighBorderColor: "#64748b",
|
||||
};
|
||||
|
||||
const UnifiedStyling = ({ product }: UnifiedStylingProps) => {
|
||||
// const [color, setColor] = useState("#333");
|
||||
const [color, setColor] = useState(product.styling?.brandColor?.light);
|
||||
const router = useRouter();
|
||||
const [unifiedStyling, setUnifiedStyling] = useState(product.styling?.unifiedStyling ?? false);
|
||||
const [allowStyleOverwrite, setAllowStyleOverwrite] = useState(
|
||||
product.styling?.allowStyleOverwrite ?? false
|
||||
);
|
||||
const [brandColor, setBrandColor] = useState(
|
||||
product.styling?.brandColor?.light ?? colorDefaults.brandColor
|
||||
);
|
||||
const [questionColor, setQuestionColor] = useState(
|
||||
product.styling?.questionColor?.light ?? colorDefaults.questionColor
|
||||
);
|
||||
const [inputColor, setInputColor] = useState(
|
||||
product.styling?.inputColor?.light ?? colorDefaults.inputColor
|
||||
);
|
||||
const [inputBorderColor, setInputBorderColor] = useState(
|
||||
product.styling?.inputBorderColor?.light ?? colorDefaults.inputBorderColor
|
||||
);
|
||||
const [cardBackgroundColor, setCardBackgroundColor] = useState(
|
||||
product.styling?.cardBackgroundColor?.light ?? colorDefaults.cardBackgroundColor
|
||||
);
|
||||
|
||||
// highlight border
|
||||
const [allowHighlightBorder, setAllowHighlightBorder] = useState(
|
||||
!!product.styling?.highlightBorderColor?.light ?? false
|
||||
);
|
||||
const [highlightBorderColor, setHighlightBorderColor] = useState(
|
||||
product.styling?.highlightBorderColor?.light ?? colorDefaults.highlighBorderColor
|
||||
);
|
||||
|
||||
const [isDarkMode, setIsDarkMode] = useState(product.styling?.isDarkModeEnabled ?? false);
|
||||
|
||||
const [brandColorDark, setBrandColorDark] = useState(product.styling?.brandColor?.dark);
|
||||
|
||||
const [questionColorDark, setQuestionColorDark] = useState(product.styling?.questionColor?.dark);
|
||||
|
||||
const [inputColorDark, setInputColorDark] = useState(product.styling?.inputColor?.dark);
|
||||
|
||||
const [inputBorderColorDark, setInputBorderColorDark] = useState(product.styling?.inputBorderColor?.dark);
|
||||
|
||||
const [cardBackgroundColorDark, setCardBackgroundColorDark] = useState(
|
||||
product.styling?.cardBackgroundColor?.dark
|
||||
);
|
||||
|
||||
const [highlightBorderColorDark, setHighlightBorderColorDark] = useState(
|
||||
product.styling?.highlightBorderColor?.dark
|
||||
);
|
||||
|
||||
const [roundness, setRoundness] = useState(product.styling?.roundness ?? 8);
|
||||
|
||||
const [linkSurveysCardArrangement, setLinkSurveysCardArrangement] = useState(
|
||||
product.styling?.cardArrangement?.linkSurveys ?? "casual"
|
||||
);
|
||||
const [inAppSurveysCardArrangement, setInAppSurveysCardArrangement] = useState(
|
||||
product.styling?.cardArrangement?.inAppSurveys ?? "casual"
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!unifiedStyling) {
|
||||
setAllowStyleOverwrite(false);
|
||||
}
|
||||
}, [unifiedStyling]);
|
||||
|
||||
const onSave = async () => {
|
||||
await updateProductAction(product.id, {
|
||||
styling: {
|
||||
unifiedStyling,
|
||||
allowStyleOverwrite,
|
||||
brandColor: {
|
||||
light: brandColor,
|
||||
dark: brandColorDark,
|
||||
},
|
||||
questionColor: {
|
||||
light: questionColor,
|
||||
dark: questionColorDark,
|
||||
},
|
||||
inputColor: {
|
||||
light: inputColor,
|
||||
dark: inputColorDark,
|
||||
},
|
||||
inputBorderColor: {
|
||||
light: inputBorderColor,
|
||||
dark: inputBorderColorDark,
|
||||
},
|
||||
cardBackgroundColor: {
|
||||
light: cardBackgroundColor,
|
||||
dark: cardBackgroundColorDark,
|
||||
},
|
||||
highlightBorderColor: allowHighlightBorder
|
||||
? {
|
||||
light: highlightBorderColor,
|
||||
dark: highlightBorderColorDark,
|
||||
}
|
||||
: undefined,
|
||||
isDarkModeEnabled: isDarkMode,
|
||||
roundness,
|
||||
cardArrangement: {
|
||||
linkSurveys: linkSurveysCardArrangement,
|
||||
inAppSurveys: inAppSurveysCardArrangement,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
toast.success("Styling updated successfully.");
|
||||
router.refresh();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
@@ -21,18 +142,29 @@ const UnifiedStyling = ({ product }: UnifiedStylingProps) => {
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-4 rounded-lg bg-slate-50 p-4">
|
||||
<div className="flex items-center gap-6">
|
||||
<Switch />
|
||||
<Switch
|
||||
checked={unifiedStyling}
|
||||
onCheckedChange={(value) => {
|
||||
setUnifiedStyling(value);
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold">Enable unified styling</h3>
|
||||
<p className="text-sm text-slate-500">Set base styles for all surveys below</p>
|
||||
<p className="text-sm text-slate-800">Set base styles for all surveys below</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-6">
|
||||
<Switch />
|
||||
<Switch
|
||||
checked={allowStyleOverwrite}
|
||||
onCheckedChange={(value) => {
|
||||
setAllowStyleOverwrite(value);
|
||||
}}
|
||||
disabled={!unifiedStyling}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold">Allow overwriting styles</h3>
|
||||
<p className="text-sm text-slate-500">
|
||||
<p className="text-sm text-slate-800">
|
||||
Activate if you want some surveys to be styled differently
|
||||
</p>
|
||||
</div>
|
||||
@@ -42,47 +174,129 @@ const UnifiedStyling = ({ product }: UnifiedStylingProps) => {
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Brand color</h3>
|
||||
<p className="text-sm text-slate-500">Change the text color of the survey questions.</p>
|
||||
<p className="text-sm text-slate-800">Change the text color of the survey questions.</p>
|
||||
</div>
|
||||
|
||||
<ColorPicker color={color} onChange={setColor} containerClass="my-0" />
|
||||
<ColorPicker color={brandColor} onChange={setBrandColor} containerClass="my-0" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Question color</h3>
|
||||
<p className="text-sm text-slate-500">Change the text color of the survey questions.</p>
|
||||
<p className="text-sm text-slate-800">Change the text color of the survey questions.</p>
|
||||
</div>
|
||||
|
||||
<ColorPicker color={color} onChange={setColor} containerClass="my-0" />
|
||||
<ColorPicker color={questionColor} onChange={setQuestionColor} containerClass="my-0" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Input color</h3>
|
||||
<p className="text-sm text-slate-500">Change the text color of the survey questions.</p>
|
||||
<p className="text-sm text-slate-800">Change the text color of the survey questions.</p>
|
||||
</div>
|
||||
|
||||
<ColorPicker color={color} onChange={setColor} containerClass="my-0" />
|
||||
<ColorPicker color={inputColor} onChange={setInputColor} containerClass="my-0" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Input border color</h3>
|
||||
<p className="text-sm text-slate-500">Change the text color of the survey questions.</p>
|
||||
<p className="text-sm text-slate-800">Change the text color of the survey questions.</p>
|
||||
</div>
|
||||
|
||||
<ColorPicker color={color} onChange={setColor} containerClass="my-0" />
|
||||
<ColorPicker color={inputBorderColor} onChange={setInputBorderColor} containerClass="my-0" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Card background color</h3>
|
||||
<p className="text-sm text-slate-500">Change the text color of the survey questions.</p>
|
||||
<p className="text-sm text-slate-800">Change the text color of the survey questions.</p>
|
||||
</div>
|
||||
|
||||
<ColorPicker color={color} onChange={setColor} containerClass="my-0" />
|
||||
<ColorPicker
|
||||
color={cardBackgroundColor}
|
||||
onChange={setCardBackgroundColor}
|
||||
containerClass="my-0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-6">
|
||||
<Switch
|
||||
checked={allowHighlightBorder}
|
||||
onCheckedChange={(value) => {
|
||||
setAllowHighlightBorder(value);
|
||||
}}
|
||||
disabled={!unifiedStyling}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold">Add highlight border</h3>
|
||||
<p className="text-sm text-slate-800">Add on outer border to your survey card</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{allowHighlightBorder && (
|
||||
<ColorPicker
|
||||
color={highlightBorderColor}
|
||||
onChange={setHighlightBorderColor}
|
||||
containerClass="my-0"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DarkModeColors
|
||||
isDarkMode={isDarkMode}
|
||||
setIsDarkMode={setIsDarkMode}
|
||||
brandColor={brandColorDark}
|
||||
cardBackgroundColor={cardBackgroundColorDark}
|
||||
highlightBorderColor={highlightBorderColorDark}
|
||||
inputBorderColor={inputBorderColorDark}
|
||||
inputColor={inputColorDark}
|
||||
questionColor={questionColorDark}
|
||||
setBrandColor={setBrandColorDark}
|
||||
setCardBackgroundColor={setCardBackgroundColorDark}
|
||||
setHighlighBorderColor={setHighlightBorderColorDark}
|
||||
setInputBorderColor={setInputBorderColorDark}
|
||||
setInputColor={setInputColorDark}
|
||||
setQuestionColor={setQuestionColorDark}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Roundness</h3>
|
||||
<p className="text-sm text-slate-800">Change the border radius of the card and the inputs.</p>
|
||||
</div>
|
||||
|
||||
<Slider
|
||||
value={[roundness]}
|
||||
max={16}
|
||||
onValueChange={(value) => setRoundness(value[0])}
|
||||
disabled={!unifiedStyling}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CardArrangement
|
||||
activeCardArrangement={linkSurveysCardArrangement}
|
||||
surveyType="link"
|
||||
setActiveCardArrangement={setLinkSurveysCardArrangement}
|
||||
/>
|
||||
|
||||
<CardArrangement
|
||||
activeCardArrangement={inAppSurveysCardArrangement}
|
||||
surveyType="web"
|
||||
setActiveCardArrangement={setInAppSurveysCardArrangement}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center justify-end gap-2">
|
||||
<Button variant="minimal" className="flex items-center gap-2">
|
||||
Reset
|
||||
<RotateCcwIcon className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<Button variant="darkCTA" onClick={onSave}>
|
||||
Save changes
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ export const ZProductUpdateInput = z.object({
|
||||
clickOutsideClose: z.boolean().optional(),
|
||||
darkOverlay: z.boolean().optional(),
|
||||
environments: z.array(ZEnvironment).optional(),
|
||||
styling: ZStyling.optional(),
|
||||
});
|
||||
|
||||
export type TProductUpdateInput = z.infer<typeof ZProductUpdateInput>;
|
||||
|
||||
@@ -8,6 +8,7 @@ export const ZStylingColor = z.object({
|
||||
});
|
||||
|
||||
export const ZCardArrangementOptions = z.enum(["casual", "straight", "simple"]);
|
||||
export type TCardArrangementOptions = z.infer<typeof ZCardArrangementOptions>;
|
||||
|
||||
export const ZCardArrangement = z.object({
|
||||
linkSurveys: ZCardArrangementOptions,
|
||||
|
||||
24
packages/ui/Slider/index.tsx
Normal file
24
packages/ui/Slider/index.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative flex w-full touch-none select-none items-center", className)}
|
||||
{...props}>
|
||||
<SliderPrimitive.Track className="relative h-1 w-full grow overflow-hidden rounded-full bg-gray-300">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-gray-300" />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb className="border-primary ring-offset-background focus-visible:ring-ring block h-5 w-5 rounded-full border-2 bg-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||
</SliderPrimitive.Root>
|
||||
));
|
||||
Slider.displayName = SliderPrimitive.Root.displayName;
|
||||
|
||||
export { Slider };
|
||||
72
packages/ui/Styling/CardArrangement.tsx
Normal file
72
packages/ui/Styling/CardArrangement.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { cn } from "@formbricks/lib/cn";
|
||||
import { TCardArrangementOptions } from "@formbricks/types/styling";
|
||||
|
||||
import { Button } from "../Button";
|
||||
|
||||
type CardArrangementProps = {
|
||||
surveyType: "link" | "web";
|
||||
activeCardArrangement: TCardArrangementOptions;
|
||||
setActiveCardArrangement: (arrangement: TCardArrangementOptions) => void;
|
||||
};
|
||||
|
||||
const CardArrangement = ({
|
||||
activeCardArrangement,
|
||||
surveyType,
|
||||
setActiveCardArrangement,
|
||||
}: CardArrangementProps) => {
|
||||
const surveyTypeDerived = useMemo(() => {
|
||||
return surveyType == "link" ? "Link" : "In App";
|
||||
}, [surveyType]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">
|
||||
Card Arrangement for {surveyTypeDerived} Surveys
|
||||
</h3>
|
||||
<p className="text-sm text-slate-800">
|
||||
How funky do you want your cards in {surveyTypeDerived} Surveys
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 rounded-md border border-slate-300 bg-white p-1">
|
||||
<Button
|
||||
variant="minimal"
|
||||
size="sm"
|
||||
className={cn(
|
||||
"flex flex-1 justify-center bg-white text-center",
|
||||
activeCardArrangement === "casual" && "bg-slate-200"
|
||||
)}
|
||||
onClick={() => setActiveCardArrangement("casual")}>
|
||||
Casual
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="minimal"
|
||||
size="sm"
|
||||
onClick={() => setActiveCardArrangement("straight")}
|
||||
className={cn(
|
||||
"flex flex-1 justify-center bg-white text-center",
|
||||
activeCardArrangement === "straight" && "bg-slate-200"
|
||||
)}>
|
||||
Straight
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="minimal"
|
||||
size="sm"
|
||||
onClick={() => setActiveCardArrangement("simple")}
|
||||
className={cn(
|
||||
"flex flex-1 justify-center bg-white text-center",
|
||||
activeCardArrangement === "simple" && "bg-slate-200"
|
||||
)}>
|
||||
Simple
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardArrangement;
|
||||
117
packages/ui/Styling/DarkModeColors.tsx
Normal file
117
packages/ui/Styling/DarkModeColors.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import { ColorPicker } from "../ColorPicker";
|
||||
import { Switch } from "../Switch";
|
||||
|
||||
const colorDefaults = {
|
||||
brandColor: "#64748b",
|
||||
questionColor: "#2b2524",
|
||||
inputColor: "#efefef",
|
||||
inputBorderColor: "#c0c0c0",
|
||||
cardBackgroundColor: "#c0c0c0",
|
||||
highlightBorderColor: "#64748b",
|
||||
};
|
||||
|
||||
const ColorSelectorWithLabel = ({
|
||||
label,
|
||||
color,
|
||||
setColor,
|
||||
}: {
|
||||
label: string;
|
||||
color: string;
|
||||
setColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">{label}</h3>
|
||||
<ColorPicker color={color} onChange={setColor} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type DarModeColorProps = {
|
||||
isDarkMode: boolean;
|
||||
setIsDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
brandColor?: string;
|
||||
setBrandColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
questionColor?: string;
|
||||
setQuestionColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
inputColor?: string;
|
||||
setInputColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
inputBorderColor?: string;
|
||||
setInputBorderColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
cardBackgroundColor?: string;
|
||||
setCardBackgroundColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
highlightBorderColor?: string;
|
||||
setHighlighBorderColor: React.Dispatch<React.SetStateAction<string>>;
|
||||
};
|
||||
|
||||
const DarkModeColors = ({
|
||||
isDarkMode,
|
||||
setIsDarkMode,
|
||||
brandColor,
|
||||
cardBackgroundColor,
|
||||
highlightBorderColor,
|
||||
inputBorderColor,
|
||||
inputColor,
|
||||
questionColor,
|
||||
setBrandColor,
|
||||
setCardBackgroundColor,
|
||||
setHighlighBorderColor,
|
||||
setInputBorderColor,
|
||||
setInputColor,
|
||||
setQuestionColor,
|
||||
}: DarModeColorProps) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 rounded-lg bg-slate-50 p-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Switch
|
||||
checked={isDarkMode}
|
||||
onCheckedChange={(value) => {
|
||||
setIsDarkMode(value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col">
|
||||
<h3 className="text-base font-semibold text-slate-900">Add "Dark Mode" Colors</h3>
|
||||
<p className="text-sm text-slate-800">Your app has a dark mode? Set a different set of colors.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isDarkMode && (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<ColorSelectorWithLabel
|
||||
label="Brand color"
|
||||
color={brandColor ?? colorDefaults.brandColor}
|
||||
setColor={setBrandColor}
|
||||
/>
|
||||
<ColorSelectorWithLabel
|
||||
label="Question color"
|
||||
color={questionColor ?? colorDefaults.questionColor}
|
||||
setColor={setQuestionColor}
|
||||
/>
|
||||
<ColorSelectorWithLabel
|
||||
label="Input color"
|
||||
color={inputColor ?? colorDefaults.inputColor}
|
||||
setColor={setInputColor}
|
||||
/>
|
||||
<ColorSelectorWithLabel
|
||||
label="Input border color"
|
||||
color={inputBorderColor ?? colorDefaults.inputBorderColor}
|
||||
setColor={setInputBorderColor}
|
||||
/>
|
||||
<ColorSelectorWithLabel
|
||||
label="Card background color"
|
||||
color={cardBackgroundColor ?? colorDefaults.cardBackgroundColor}
|
||||
setColor={setCardBackgroundColor}
|
||||
/>
|
||||
<ColorSelectorWithLabel
|
||||
label="Highlight border color"
|
||||
color={highlightBorderColor ?? colorDefaults.highlightBorderColor}
|
||||
setColor={setHighlighBorderColor}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DarkModeColors;
|
||||
Reference in New Issue
Block a user