Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae6656e0ec | |||
| 7e9eae0ef2 | |||
| 6b28abc405 |
+15
-15
@@ -26,13 +26,13 @@ export default function Navbar() {
|
|||||||
const { width } = useWindowDimensions();
|
const { width } = useWindowDimensions();
|
||||||
|
|
||||||
const handleToggle = () => {
|
const handleToggle = () => {
|
||||||
if (settings.theme === "dark") {
|
const [colorTheme, mode] = (settings.theme || "default-light").split('-');
|
||||||
updateSettings({ theme: "light" });
|
const newMode = mode === "dark" ? "light" : "dark";
|
||||||
} else {
|
const newTheme = `${colorTheme}-${newMode}`;
|
||||||
updateSettings({ theme: "dark" });
|
updateSettings({ theme: newTheme });
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSidebar(false);
|
setSidebar(false);
|
||||||
}, [width]);
|
}, [width]);
|
||||||
@@ -135,16 +135,16 @@ export default function Navbar() {
|
|||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
(document?.activeElement as HTMLElement)?.blur();
|
(document?.activeElement as HTMLElement)?.blur();
|
||||||
handleToggle();
|
handleToggle();
|
||||||
}}
|
}}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="button"
|
role="button"
|
||||||
>
|
>
|
||||||
Switch to {settings.theme === "light" ? "Dark" : "Light"}
|
Switch to {(settings.theme || "default-light").endsWith("-dark") ? "Light" : "Dark"}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -2,39 +2,46 @@ import useLocalSettingsStore from "@/store/localSettings";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ToggleDarkMode({ className }: Props) {
|
export default function ToggleDarkMode({ className }: Props) {
|
||||||
const { settings, updateSettings } = useLocalSettingsStore();
|
const { updateSettings } = useLocalSettingsStore();
|
||||||
|
const [theme, setTheme] = useState('default-light');
|
||||||
|
|
||||||
const [theme, setTheme] = useState(localStorage.getItem("theme"));
|
useEffect(() => {
|
||||||
|
const storedTheme = localStorage.getItem("theme");
|
||||||
|
if (storedTheme) {
|
||||||
|
setTheme(storedTheme);
|
||||||
|
} else {
|
||||||
|
// Default theme if not set in localStorage
|
||||||
|
localStorage.setItem("theme", "default-light");
|
||||||
|
setTheme("default-light");
|
||||||
|
}
|
||||||
|
console.log("Initial theme from localStorage:", storedTheme || "default-light");
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleToggle = (e: any) => {
|
const handleToggle = () => {
|
||||||
setTheme(e.target.checked ? "dark" : "light");
|
const [currentColorTheme, currentMode] = theme.split('-');
|
||||||
};
|
const newMode = currentMode === 'light' ? 'dark' : 'light';
|
||||||
|
const newTheme = `${currentColorTheme}-${newMode}`;
|
||||||
|
|
||||||
useEffect(() => {
|
setTheme(newTheme);
|
||||||
updateSettings({ theme: theme as string });
|
localStorage.setItem("theme", newTheme);
|
||||||
}, [theme]);
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
|
updateSettings({ theme: newTheme });
|
||||||
|
console.log("New theme set:", newTheme);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
const isDarkMode = theme.endsWith('-dark');
|
||||||
<div
|
|
||||||
className="tooltip tooltip-bottom"
|
return (
|
||||||
data-tip={`Switch to ${settings.theme === "light" ? "Dark" : "Light"}`}
|
<div className="tooltip tooltip-bottom" data-tip={`Switch to ${isDarkMode ? "Light" : "Dark"}`}>
|
||||||
>
|
<label className={`swap swap-rotate btn-square text-neutral btn btn-ghost btn-sm ${className}`}>
|
||||||
<label
|
<input type="checkbox" onChange={handleToggle} className="theme-controller" checked={isDarkMode} />
|
||||||
className={`swap swap-rotate btn-square text-neutral btn btn-ghost btn-sm ${className}`}
|
<i className="bi-sun-fill text-xl swap-on"></i>
|
||||||
>
|
<i className="bi-moon-fill text-xl swap-off"></i>
|
||||||
<input
|
</label>
|
||||||
type="checkbox"
|
</div>
|
||||||
onChange={handleToggle}
|
);
|
||||||
className="theme-controller"
|
|
||||||
checked={localStorage.getItem("theme") === "light" ? false : true}
|
|
||||||
/>
|
|
||||||
<i className="bi-sun-fill text-xl swap-on"></i>
|
|
||||||
<i className="bi-moon-fill text-xl swap-off"></i>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-45
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import "@/styles/globals.css";
|
import "@/styles/globals.css";
|
||||||
import "bootstrap-icons/font/bootstrap-icons.css";
|
import "bootstrap-icons/font/bootstrap-icons.css";
|
||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
@@ -9,50 +9,62 @@ import { Toaster } from "react-hot-toast";
|
|||||||
import { Session } from "next-auth";
|
import { Session } from "next-auth";
|
||||||
|
|
||||||
export default function App({
|
export default function App({
|
||||||
Component,
|
Component,
|
||||||
pageProps,
|
pageProps,
|
||||||
}: AppProps<{
|
}: AppProps<{
|
||||||
session: Session;
|
session: Session;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
|
||||||
<SessionProvider
|
useEffect(() => {
|
||||||
session={pageProps.session}
|
let theme = localStorage.getItem("theme");
|
||||||
refetchOnWindowFocus={false}
|
if (!theme || !theme.includes("-")) {
|
||||||
basePath="/api/v1/auth"
|
theme = "default-dark"; // Default theme
|
||||||
>
|
localStorage.setItem("theme", theme);
|
||||||
<Head>
|
}
|
||||||
<title>Linkwarden</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
document.documentElement.setAttribute('data-theme', theme);
|
||||||
<link
|
}, []);
|
||||||
rel="apple-touch-icon"
|
|
||||||
sizes="180x180"
|
|
||||||
href="/apple-touch-icon.png"
|
return (
|
||||||
/>
|
<SessionProvider
|
||||||
<link
|
session={pageProps.session}
|
||||||
rel="icon"
|
refetchOnWindowFocus={false}
|
||||||
type="image/png"
|
basePath="/api/v1/auth"
|
||||||
sizes="32x32"
|
>
|
||||||
href="/favicon-32x32.png"
|
<Head>
|
||||||
/>
|
<title>Linkwarden</title>
|
||||||
<link
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
rel="icon"
|
<link
|
||||||
type="image/png"
|
rel="apple-touch-icon"
|
||||||
sizes="16x16"
|
sizes="180x180"
|
||||||
href="/favicon-16x16.png"
|
href="/apple-touch-icon.png"
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link
|
||||||
</Head>
|
rel="icon"
|
||||||
<AuthRedirect>
|
type="image/png"
|
||||||
<Toaster
|
sizes="32x32"
|
||||||
position="top-center"
|
href="/favicon-32x32.png"
|
||||||
reverseOrder={false}
|
/>
|
||||||
toastOptions={{
|
<link
|
||||||
className:
|
rel="icon"
|
||||||
"border border-sky-100 dark:border-neutral-700 dark:bg-neutral-800 dark:text-white",
|
type="image/png"
|
||||||
}}
|
sizes="16x16"
|
||||||
/>
|
href="/favicon-16x16.png"
|
||||||
<Component {...pageProps} />
|
/>
|
||||||
</AuthRedirect>
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
</SessionProvider>
|
</Head>
|
||||||
);
|
<AuthRedirect>
|
||||||
|
<Toaster
|
||||||
|
position="top-center"
|
||||||
|
reverseOrder={false}
|
||||||
|
toastOptions={{
|
||||||
|
className:
|
||||||
|
"border border-sky-100 dark:border-neutral-700 dark:bg-neutral-800 dark:text-white",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</AuthRedirect>
|
||||||
|
</SessionProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,12 +101,14 @@ export default function Index() {
|
|||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<div
|
<div
|
||||||
className="h-[60rem] p-5 flex gap-3 flex-col"
|
className="h-[60rem] p-5 flex gap-3 flex-col"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `linear-gradient(${activeCollection?.color}20 10%, ${
|
backgroundImage: `linear-gradient(${activeCollection?.color}20 10%, ${
|
||||||
settings.theme === "dark" ? "#262626" : "#f3f4f6"
|
(settings.theme || "default-light").endsWith("-dark") ? "#262626" : "#f3f4f6"
|
||||||
} 13rem, ${settings.theme === "dark" ? "#171717" : "#ffffff"} 100%)`,
|
} 13rem, ${
|
||||||
}}
|
(settings.theme || "default-light").endsWith("-dark") ? "#171717" : "#ffffff"
|
||||||
|
} 100%)`,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{activeCollection && (
|
{activeCollection && (
|
||||||
<div className="flex gap-3 items-start justify-between">
|
<div className="flex gap-3 items-start justify-between">
|
||||||
|
|||||||
+258
-258
@@ -19,293 +19,293 @@ import ViewDropdown from "@/components/ViewDropdown";
|
|||||||
// import GridView from "@/components/LinkViews/Layouts/GridView";
|
// import GridView from "@/components/LinkViews/Layouts/GridView";
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const { collections } = useCollectionStore();
|
const { collections } = useCollectionStore();
|
||||||
const { links } = useLinkStore();
|
const { links } = useLinkStore();
|
||||||
const { tags } = useTagStore();
|
const { tags } = useTagStore();
|
||||||
|
|
||||||
const [numberOfLinks, setNumberOfLinks] = useState(0);
|
const [numberOfLinks, setNumberOfLinks] = useState(0);
|
||||||
|
|
||||||
const [showLinks, setShowLinks] = useState(3);
|
const [showLinks, setShowLinks] = useState(3);
|
||||||
|
|
||||||
useLinks({ pinnedOnly: true, sort: 0 });
|
useLinks({ pinnedOnly: true, sort: 0 });
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNumberOfLinks(
|
setNumberOfLinks(
|
||||||
collections.reduce(
|
collections.reduce(
|
||||||
(accumulator, collection) =>
|
(accumulator, collection) =>
|
||||||
accumulator + (collection._count as any).links,
|
accumulator + (collection._count as any).links,
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}, [collections]);
|
}, [collections]);
|
||||||
|
|
||||||
const handleNumberOfLinksToShow = () => {
|
const handleNumberOfLinksToShow = () => {
|
||||||
if (window.innerWidth > 1900) {
|
if (window.innerWidth > 1900) {
|
||||||
setShowLinks(8);
|
setShowLinks(8);
|
||||||
} else if (window.innerWidth > 1280) {
|
} else if (window.innerWidth > 1280) {
|
||||||
setShowLinks(6);
|
setShowLinks(6);
|
||||||
} else if (window.innerWidth > 650) {
|
} else if (window.innerWidth > 650) {
|
||||||
setShowLinks(4);
|
setShowLinks(4);
|
||||||
} else setShowLinks(3);
|
} else setShowLinks(3);
|
||||||
};
|
};
|
||||||
|
|
||||||
const { width } = useWindowDimensions();
|
const { width } = useWindowDimensions();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleNumberOfLinksToShow();
|
handleNumberOfLinksToShow();
|
||||||
}, [width]);
|
}, [width]);
|
||||||
|
|
||||||
const importBookmarks = async (e: any, format: MigrationFormat) => {
|
const importBookmarks = async (e: any, format: MigrationFormat) => {
|
||||||
const file: File = e.target.files[0];
|
const file: File = e.target.files[0];
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
reader.readAsText(file, "UTF-8");
|
reader.readAsText(file, "UTF-8");
|
||||||
reader.onload = async function (e) {
|
reader.onload = async function (e) {
|
||||||
const load = toast.loading("Importing...");
|
const load = toast.loading("Importing...");
|
||||||
|
|
||||||
const request: string = e.target?.result as string;
|
const request: string = e.target?.result as string;
|
||||||
|
|
||||||
const body: MigrationRequest = {
|
const body: MigrationRequest = {
|
||||||
format,
|
format,
|
||||||
data: request,
|
data: request,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await fetch("/api/v1/migration", {
|
const response = await fetch("/api/v1/migration", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
toast.dismiss(load);
|
toast.dismiss(load);
|
||||||
|
|
||||||
toast.success("Imported the Bookmarks! Reloading the page...");
|
toast.success("Imported the Bookmarks! Reloading the page...");
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.reload();
|
location.reload();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
};
|
};
|
||||||
reader.onerror = function (e) {
|
reader.onerror = function (e) {
|
||||||
console.log("Error:", e);
|
console.log("Error:", e);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [newLinkModal, setNewLinkModal] = useState(false);
|
const [newLinkModal, setNewLinkModal] = useState(false);
|
||||||
|
|
||||||
const [viewMode, setViewMode] = useState<string>(
|
const [viewMode, setViewMode] = useState<string>(
|
||||||
localStorage.getItem("viewMode") || ViewMode.Card
|
localStorage.getItem("viewMode") || ViewMode.Card
|
||||||
);
|
);
|
||||||
|
|
||||||
const linkView = {
|
const linkView = {
|
||||||
[ViewMode.Card]: CardView,
|
[ViewMode.Card]: CardView,
|
||||||
// [ViewMode.Grid]: GridView,
|
// [ViewMode.Grid]: GridView,
|
||||||
[ViewMode.List]: ListView,
|
[ViewMode.List]: ListView,
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const LinkComponent = linkView[viewMode];
|
const LinkComponent = linkView[viewMode];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout>
|
<MainLayout>
|
||||||
<div style={{ flex: "1 1 auto" }} className="p-5 flex flex-col gap-5">
|
<div style={{ flex: "1 1 auto" }} className="p-5 flex flex-col gap-5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<PageHeader
|
<PageHeader
|
||||||
icon={"bi-house "}
|
icon={"bi-house "}
|
||||||
title={"Dashboard"}
|
title={"Dashboard"}
|
||||||
description={"A brief overview of your data"}
|
description={"A brief overview of your data"}
|
||||||
/>
|
/>
|
||||||
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
<ViewDropdown viewMode={viewMode} setViewMode={setViewMode} />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div className="flex justify-evenly flex-col xl:flex-row xl:items-center gap-2 xl:w-full h-full rounded-2xl p-8 border border-neutral-content bg-base-200">
|
|
||||||
<DashboardItem
|
|
||||||
name={numberOfLinks === 1 ? "Link" : "Links"}
|
|
||||||
value={numberOfLinks}
|
|
||||||
icon={"bi-link-45deg"}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="divider xl:divider-horizontal"></div>
|
|
||||||
|
|
||||||
<DashboardItem
|
|
||||||
name={collections.length === 1 ? "Collection" : "Collections"}
|
|
||||||
value={collections.length}
|
|
||||||
icon={"bi-folder"}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="divider xl:divider-horizontal"></div>
|
|
||||||
|
|
||||||
<DashboardItem
|
|
||||||
name={tags.length === 1 ? "Tag" : "Tags"}
|
|
||||||
value={tags.length}
|
|
||||||
icon={"bi-hash"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div className="flex gap-2 items-center">
|
|
||||||
<PageHeader
|
|
||||||
icon={"bi-clock-history"}
|
|
||||||
title={"Recent"}
|
|
||||||
description={"Recently added Links"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Link
|
|
||||||
href="/links"
|
|
||||||
className="flex items-center text-sm text-black/75 dark:text-white/75 gap-2 cursor-pointer"
|
|
||||||
>
|
|
||||||
View All
|
|
||||||
<i className="bi-chevron-right text-sm"></i>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
style={{ flex: "0 1 auto" }}
|
|
||||||
className="flex flex-col 2xl:flex-row items-start 2xl:gap-2"
|
|
||||||
>
|
|
||||||
{links[0] ? (
|
|
||||||
<div className="w-full">
|
|
||||||
<LinkComponent links={links.slice(0, showLinks)} />
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-evenly flex-col xl:flex-row xl:items-center gap-2 xl:w-full h-full rounded-2xl p-8 border border-neutral-content bg-base-200">
|
||||||
|
<DashboardItem
|
||||||
|
name={numberOfLinks === 1 ? "Link" : "Links"}
|
||||||
|
value={numberOfLinks}
|
||||||
|
icon={"bi-link-45deg"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="divider xl:divider-horizontal"></div>
|
||||||
|
|
||||||
|
<DashboardItem
|
||||||
|
name={collections.length === 1 ? "Collection" : "Collections"}
|
||||||
|
value={collections.length}
|
||||||
|
icon={"bi-folder"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="divider xl:divider-horizontal"></div>
|
||||||
|
|
||||||
|
<DashboardItem
|
||||||
|
name={tags.length === 1 ? "Tag" : "Tags"}
|
||||||
|
value={tags.length}
|
||||||
|
icon={"bi-hash"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<PageHeader
|
||||||
|
icon={"bi-clock-history"}
|
||||||
|
title={"Recent"}
|
||||||
|
description={"Recently added Links"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Link
|
||||||
|
href="/links"
|
||||||
|
className="flex items-center text-sm text-neutral gap-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
View All
|
||||||
|
<i className="bi-chevron-right text-sm"></i>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{ flex: "1 1 auto" }}
|
style={{ flex: "0 1 auto" }}
|
||||||
className="sky-shadow flex flex-col justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-2xl bg-base-200"
|
className="flex flex-col 2xl:flex-row items-start 2xl:gap-2"
|
||||||
>
|
>
|
||||||
<p className="text-center text-2xl">
|
{links[0] ? (
|
||||||
View Your Recently Added Links Here!
|
<div className="w-full">
|
||||||
</p>
|
<LinkComponent links={links.slice(0, showLinks)} />
|
||||||
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm mt-2">
|
|
||||||
This section will view your latest added Links across every
|
|
||||||
Collections you have access to.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="text-center w-full mt-4 flex flex-wrap gap-4 justify-center">
|
|
||||||
<div
|
|
||||||
onClick={() => {
|
|
||||||
setNewLinkModal(true);
|
|
||||||
}}
|
|
||||||
className="inline-flex items-center gap-2 text-sm btn btn-accent dark:border-violet-400 text-white"
|
|
||||||
>
|
|
||||||
<i className="bi-plus-lg text-xl duration-100"></i>
|
|
||||||
<span className="group-hover:opacity-0 text-right duration-100">
|
|
||||||
Add New Link
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="dropdown dropdown-bottom">
|
|
||||||
<div
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
className="inline-flex items-center gap-2 text-sm btn btn-outline btn-neutral"
|
|
||||||
id="import-dropdown"
|
|
||||||
>
|
|
||||||
<i className="bi-cloud-upload text-xl duration-100"></i>
|
|
||||||
<p>Import From</p>
|
|
||||||
</div>
|
</div>
|
||||||
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1 w-60">
|
) : (
|
||||||
<li>
|
<div
|
||||||
<label
|
style={{ flex: "1 1 auto" }}
|
||||||
tabIndex={0}
|
className="sky-shadow flex flex-col justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-2xl bg-base-200"
|
||||||
role="button"
|
>
|
||||||
htmlFor="import-linkwarden-file"
|
<p className="text-center text-2xl">
|
||||||
title="JSON File"
|
View Your Recently Added Links Here!
|
||||||
>
|
</p>
|
||||||
From Linkwarden
|
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm mt-2">
|
||||||
<input
|
This section will view your latest added Links across every
|
||||||
type="file"
|
Collections you have access to.
|
||||||
name="photo"
|
</p>
|
||||||
id="import-linkwarden-file"
|
|
||||||
accept=".json"
|
|
||||||
className="hidden"
|
|
||||||
onChange={(e) =>
|
|
||||||
importBookmarks(e, MigrationFormat.linkwarden)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label
|
|
||||||
tabIndex={0}
|
|
||||||
role="button"
|
|
||||||
htmlFor="import-html-file"
|
|
||||||
title="HTML File"
|
|
||||||
>
|
|
||||||
From Bookmarks HTML file
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
name="photo"
|
|
||||||
id="import-html-file"
|
|
||||||
accept=".html"
|
|
||||||
className="hidden"
|
|
||||||
onChange={(e) =>
|
|
||||||
importBookmarks(e, MigrationFormat.htmlFile)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex justify-between items-center">
|
<div className="text-center w-full mt-4 flex flex-wrap gap-4 justify-center">
|
||||||
<div className="flex gap-2 items-center">
|
<div
|
||||||
<PageHeader
|
onClick={() => {
|
||||||
icon={"bi-pin-angle"}
|
setNewLinkModal(true);
|
||||||
title={"Pinned"}
|
}}
|
||||||
description={"Your pinned Links"}
|
className="inline-flex items-center gap-2 text-sm btn btn-accent dark:border-violet-400 text-white"
|
||||||
/>
|
>
|
||||||
</div>
|
<i className="bi-plus-lg text-xl duration-100"></i>
|
||||||
<Link
|
<span className="group-hover:opacity-0 text-right duration-100">
|
||||||
href="/links/pinned"
|
Add New Link
|
||||||
className="flex items-center text-sm text-black/75 dark:text-white/75 gap-2 cursor-pointer"
|
</span>
|
||||||
>
|
</div>
|
||||||
View All
|
|
||||||
<i className="bi-chevron-right text-sm "></i>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div className="dropdown dropdown-bottom">
|
||||||
style={{ flex: "1 1 auto" }}
|
<div
|
||||||
className="flex flex-col 2xl:flex-row items-start 2xl:gap-2"
|
tabIndex={0}
|
||||||
>
|
role="button"
|
||||||
{links.some((e) => e.pinnedBy && e.pinnedBy[0]) ? (
|
className="inline-flex items-center gap-2 text-sm btn btn-outline btn-neutral"
|
||||||
<div className="w-full">
|
id="import-dropdown"
|
||||||
<div
|
>
|
||||||
className={`grid min-[1900px]:grid-cols-4 xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5 w-full`}
|
<i className="bi-cloud-upload text-xl duration-100"></i>
|
||||||
>
|
<p>Import From</p>
|
||||||
{links
|
</div>
|
||||||
.filter((e) => e.pinnedBy && e.pinnedBy[0])
|
<ul className="shadow menu dropdown-content z-[1] bg-base-200 border border-neutral-content rounded-box mt-1 w-60">
|
||||||
.map((e, i) => <LinkCard key={i} link={e} count={i} />)
|
<li>
|
||||||
.slice(0, showLinks)}
|
<label
|
||||||
</div>
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
htmlFor="import-linkwarden-file"
|
||||||
|
title="JSON File"
|
||||||
|
>
|
||||||
|
From Linkwarden
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
name="photo"
|
||||||
|
id="import-linkwarden-file"
|
||||||
|
accept=".json"
|
||||||
|
className="hidden"
|
||||||
|
onChange={(e) =>
|
||||||
|
importBookmarks(e, MigrationFormat.linkwarden)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
htmlFor="import-html-file"
|
||||||
|
title="HTML File"
|
||||||
|
>
|
||||||
|
From Bookmarks HTML file
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
name="photo"
|
||||||
|
id="import-html-file"
|
||||||
|
accept=".html"
|
||||||
|
className="hidden"
|
||||||
|
onChange={(e) =>
|
||||||
|
importBookmarks(e, MigrationFormat.htmlFile)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<PageHeader
|
||||||
|
icon={"bi-pin-angle"}
|
||||||
|
title={"Pinned"}
|
||||||
|
description={"Your pinned Links"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Link
|
||||||
|
href="/links/pinned"
|
||||||
|
className="flex items-center text-sm text-neutral gap-2 cursor-pointer"
|
||||||
|
>
|
||||||
|
View All
|
||||||
|
<i className="bi-chevron-right text-sm "></i>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{ flex: "1 1 auto" }}
|
style={{ flex: "1 1 auto" }}
|
||||||
className="sky-shadow flex flex-col justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-2xl bg-base-200"
|
className="flex flex-col 2xl:flex-row items-start 2xl:gap-2"
|
||||||
>
|
>
|
||||||
<p className="text-center text-2xl">
|
{links.some((e) => e.pinnedBy && e.pinnedBy[0]) ? (
|
||||||
Pin Your Favorite Links Here!
|
<div className="w-full">
|
||||||
</p>
|
<div
|
||||||
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm mt-2">
|
className={`grid min-[1900px]:grid-cols-4 xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5 w-full`}
|
||||||
You can Pin your favorite Links by clicking on the three dots on
|
>
|
||||||
each Link and clicking{" "}
|
{links
|
||||||
<span className="font-semibold">Pin to Dashboard</span>.
|
.filter((e) => e.pinnedBy && e.pinnedBy[0])
|
||||||
</p>
|
.map((e, i) => <LinkCard key={i} link={e} count={i} />)
|
||||||
|
.slice(0, showLinks)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{ flex: "1 1 auto" }}
|
||||||
|
className="sky-shadow flex flex-col justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-2xl bg-base-200"
|
||||||
|
>
|
||||||
|
<p className="text-center text-2xl">
|
||||||
|
Pin Your Favorite Links Here!
|
||||||
|
</p>
|
||||||
|
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm mt-2">
|
||||||
|
You can Pin your favorite Links by clicking on the three dots on
|
||||||
|
each Link and clicking{" "}
|
||||||
|
<span className="font-semibold">Pin to Dashboard</span>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
{newLinkModal ? (
|
||||||
</div>
|
<NewLinkModal onClose={() => setNewLinkModal(false)} />
|
||||||
{newLinkModal ? (
|
) : undefined}
|
||||||
<NewLinkModal onClose={() => setNewLinkModal(false)} />
|
</MainLayout>
|
||||||
) : undefined}
|
);
|
||||||
</MainLayout>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
|
|
||||||
import SettingsLayout from "@/layouts/SettingsLayout";
|
import SettingsLayout from "@/layouts/SettingsLayout";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import useAccountStore from "@/store/account";
|
import useAccountStore from "@/store/account";
|
||||||
import { AccountSettings } from "@/types/global";
|
import { AccountSettings } from "@/types/global";
|
||||||
import { toast } from "react-hot-toast";
|
import { toast } from "react-hot-toast";
|
||||||
import React from "react";
|
|
||||||
import useLocalSettingsStore from "@/store/localSettings";
|
import useLocalSettingsStore from "@/store/localSettings";
|
||||||
|
|
||||||
export default function Appearance() {
|
export default function Appearance() {
|
||||||
const { updateSettings } = useLocalSettingsStore();
|
const { updateSettings } = useLocalSettingsStore();
|
||||||
const submit = async () => {
|
const { account, updateAccount } = useAccountStore();
|
||||||
|
const submit = async () => {
|
||||||
setSubmitLoader(true);
|
setSubmitLoader(true);
|
||||||
|
|
||||||
const load = toast.loading("Applying...");
|
const load = toast.loading("Applying...");
|
||||||
|
|
||||||
const response = await updateAccount({
|
const response = await updateAccount({
|
||||||
@@ -18,89 +18,108 @@ export default function Appearance() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
toast.dismiss(load);
|
toast.dismiss(load);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
toast.success("Settings Applied!");
|
toast.success("Settings Applied!");
|
||||||
} else toast.error(response.data as string);
|
} else toast.error(response.data as string);
|
||||||
setSubmitLoader(false);
|
setSubmitLoader(false);
|
||||||
};
|
};
|
||||||
|
const [submitLoader, setSubmitLoader] = useState(false);
|
||||||
|
const [user, setUser] = useState<AccountSettings>(
|
||||||
|
!objectIsEmpty(account)
|
||||||
|
? account
|
||||||
|
: ({
|
||||||
|
// @ts-ignore
|
||||||
|
id: null,
|
||||||
|
name: "",
|
||||||
|
username: "",
|
||||||
|
email: "",
|
||||||
|
emailVerified: null,
|
||||||
|
blurredFavicons: null,
|
||||||
|
image: "",
|
||||||
|
isPrivate: true,
|
||||||
|
// @ts-ignore
|
||||||
|
createdAt: null,
|
||||||
|
whitelistedUsers: [],
|
||||||
|
} as unknown as AccountSettings)
|
||||||
|
);
|
||||||
|
|
||||||
const [submitLoader, setSubmitLoader] = useState(false);
|
// Combine colorTheme and mode into a single state
|
||||||
|
const [theme, setTheme] = useState(localStorage.getItem("theme") || "default-dark");
|
||||||
|
|
||||||
const { account, updateAccount } = useAccountStore();
|
function objectIsEmpty(obj: object) {
|
||||||
|
return Object.keys(obj).length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
const [user, setUser] = useState<AccountSettings>(
|
useEffect(() => {
|
||||||
!objectIsEmpty(account)
|
if (!objectIsEmpty(account)) setUser({ ...account });
|
||||||
? account
|
}, [account]);
|
||||||
: ({
|
|
||||||
// @ts-ignore
|
|
||||||
id: null,
|
|
||||||
name: "",
|
|
||||||
username: "",
|
|
||||||
email: "",
|
|
||||||
emailVerified: null,
|
|
||||||
blurredFavicons: null,
|
|
||||||
image: "",
|
|
||||||
isPrivate: true,
|
|
||||||
// @ts-ignore
|
|
||||||
createdAt: null,
|
|
||||||
whitelistedUsers: [],
|
|
||||||
} as unknown as AccountSettings)
|
|
||||||
);
|
|
||||||
|
|
||||||
function objectIsEmpty(obj: object) {
|
const handleThemeChange = (newThemePart: string, isColorTheme: boolean) => {
|
||||||
return Object.keys(obj).length === 0;
|
const currentTheme = localStorage.getItem("theme") || "default-light";
|
||||||
}
|
const [currentColorTheme, currentMode] = currentTheme.split('-');
|
||||||
|
const newTheme = isColorTheme ? `${newThemePart}-${currentMode}` : `${currentColorTheme}-${newThemePart}`;
|
||||||
|
|
||||||
useEffect(() => {
|
localStorage.setItem("theme", newTheme);
|
||||||
if (!objectIsEmpty(account)) setUser({ ...account });
|
document.documentElement.setAttribute('data-theme', newTheme);
|
||||||
}, [account]);
|
updateSettings({ theme: newTheme });
|
||||||
|
|
||||||
return (
|
// Update the theme state
|
||||||
<SettingsLayout>
|
setTheme(newTheme);
|
||||||
<p className="capitalize text-3xl font-thin inline">Appearance</p>
|
};
|
||||||
|
|
||||||
<div className="divider my-3"></div>
|
|
||||||
|
|
||||||
<div className="flex flex-col gap-5">
|
|
||||||
<div>
|
|
||||||
<p className="mb-3">Select Theme</p>
|
|
||||||
<div className="flex gap-3 w-full">
|
|
||||||
<div
|
|
||||||
className={`w-full text-center outline-solid outline-neutral-content outline dark:outline-neutral-700 h-36 duration-100 rounded-md flex items-center justify-center cursor-pointer select-none bg-black ${
|
|
||||||
localStorage.getItem("theme") === "dark"
|
|
||||||
? "dark:outline-primary text-primary"
|
|
||||||
: "text-white"
|
|
||||||
}`}
|
|
||||||
onClick={() => updateSettings({ theme: "dark" })}
|
|
||||||
>
|
|
||||||
<i className="bi-moon-fill text-6xl"></i>
|
|
||||||
<p className="ml-2 text-2xl">Dark</p>
|
|
||||||
|
|
||||||
{/* <hr className="my-3 outline-1 outline-neutral-content dark:outline-neutral-700" /> */}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsLayout>
|
||||||
|
<p className="capitalize text-3xl font-thin inline">Appearance</p>
|
||||||
|
<div className="divider my-3"></div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-5">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<p className="mb-3">Select Mode</p>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
{["light", "dark"].map((modeOption) => (
|
||||||
|
<button
|
||||||
|
key={modeOption}
|
||||||
|
onClick={() => handleThemeChange(modeOption, false)}
|
||||||
|
className={`w-full text-center h-36 duration-100 rounded-md flex items-center justify-center cursor-pointer select-none ${theme.endsWith(modeOption) ? "ring-2 ring-primary" : "ring-2 ring-neutral"}`}
|
||||||
|
>
|
||||||
|
{modeOption === 'light' ?
|
||||||
|
<i className={`bi-sun-fill text-6xl ${theme.endsWith(modeOption) ? "text-primary" : ""}`}></i> :
|
||||||
|
<i className={`bi-moon-fill text-6xl ${theme.endsWith(modeOption) ? "text-primary" : ""}`}></i>}
|
||||||
|
<p className="ml-2 text-2xl">{modeOption.charAt(0).toUpperCase() + modeOption.slice(1)}</p>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
className={`w-full text-center outline-solid outline-neutral-content outline dark:outline-neutral-700 h-36 duration-100 rounded-md flex items-center justify-center cursor-pointer select-none bg-white ${
|
|
||||||
localStorage.getItem("theme") === "light"
|
|
||||||
? "outline-primary text-primary"
|
|
||||||
: "text-black"
|
|
||||||
}`}
|
|
||||||
onClick={() => updateSettings({ theme: "light" })}
|
|
||||||
>
|
|
||||||
<i className="bi-sun-fill text-6xl"></i>
|
|
||||||
<p className="ml-2 text-2xl">Light</p>
|
|
||||||
{/* <hr className="my-3 outline-1 outline-neutral-content dark:outline-neutral-700" /> */}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* <SubmitButton
|
<div>
|
||||||
onClick={submit}
|
<p className="mb-3">Select Color Theme</p>
|
||||||
loading={submitLoader}
|
<div className="grid grid-cols-4 gap-3">
|
||||||
label="Save"
|
{["default", "red", "green", "orange"].map((colorTheme) => (
|
||||||
className="mt-2 mx-auto lg:mx-0"
|
<button
|
||||||
/> */}
|
key={colorTheme}
|
||||||
</div>
|
onClick={() => handleThemeChange(colorTheme, true)}
|
||||||
</SettingsLayout>
|
className={`w-full text-center h-36 duration-100 rounded-md flex items-center justify-center cursor-pointer select-none ${theme.startsWith(colorTheme) ? "ring-2 ring-primary" : "ring-2 ring-neutral"}`}
|
||||||
);
|
>
|
||||||
|
{colorTheme.charAt(0).toUpperCase() + colorTheme.slice(1)}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={submit}
|
||||||
|
disabled={submitLoader}
|
||||||
|
className="mt-2 mx-auto lg:mx-0 bg-primary text-white rounded-md px-4 py-2"
|
||||||
|
>
|
||||||
|
{submitLoader ? "Saving..." : "Save"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</SettingsLayout>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+21
-16
@@ -18,34 +18,39 @@ const useLocalSettingsStore = create<LocalSettingsStore>((set) => ({
|
|||||||
viewMode: "",
|
viewMode: "",
|
||||||
},
|
},
|
||||||
updateSettings: async (newSettings) => {
|
updateSettings: async (newSettings) => {
|
||||||
if (
|
if (newSettings.theme) {
|
||||||
newSettings.theme &&
|
|
||||||
newSettings.theme !== localStorage.getItem("theme")
|
|
||||||
) {
|
|
||||||
localStorage.setItem("theme", newSettings.theme);
|
localStorage.setItem("theme", newSettings.theme);
|
||||||
|
document.documentElement.setAttribute('data-theme', newSettings.theme);
|
||||||
|
|
||||||
const localTheme = localStorage.getItem("theme") || "";
|
if (newSettings.theme.endsWith("-dark")) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
document.querySelector("html")?.setAttribute("data-theme", localTheme);
|
} else {
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (newSettings.viewMode) {
|
||||||
newSettings.viewMode &&
|
|
||||||
newSettings.viewMode !== localStorage.getItem("viewMode")
|
|
||||||
) {
|
|
||||||
localStorage.setItem("viewMode", newSettings.viewMode);
|
localStorage.setItem("viewMode", newSettings.viewMode);
|
||||||
|
|
||||||
// const localTheme = localStorage.getItem("viewMode") || "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set((state) => ({ settings: { ...state.settings, ...newSettings } }));
|
set((state) => ({ settings: { ...state.settings, ...newSettings } }));
|
||||||
},
|
},
|
||||||
setSettings: async () => {
|
setSettings: async () => {
|
||||||
if (!localStorage.getItem("theme")) {
|
let theme = localStorage.getItem("theme");
|
||||||
localStorage.setItem("theme", "dark");
|
if (!theme || !theme.includes("-")) {
|
||||||
|
theme = "default-dark"; // Default theme
|
||||||
|
localStorage.setItem("theme", theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
const localTheme = localStorage.getItem("theme") || "";
|
const localTheme = theme;
|
||||||
|
|
||||||
|
document.documentElement.setAttribute('data-theme', localTheme);
|
||||||
|
|
||||||
|
if (localTheme.endsWith("-dark")) {
|
||||||
|
document.documentElement.classList.add("dark");
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove("dark");
|
||||||
|
}
|
||||||
|
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
settings: { ...state.settings, theme: localTheme },
|
settings: { ...state.settings, theme: localTheme },
|
||||||
|
|||||||
+121
-18
@@ -5,7 +5,7 @@ module.exports = {
|
|||||||
daisyui: {
|
daisyui: {
|
||||||
themes: [
|
themes: [
|
||||||
{
|
{
|
||||||
light: {
|
"default-light": {
|
||||||
primary: "#0369a1",
|
primary: "#0369a1",
|
||||||
secondary: "#0891b2",
|
secondary: "#0891b2",
|
||||||
accent: "#6d28d9",
|
accent: "#6d28d9",
|
||||||
@@ -21,7 +21,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dark: {
|
"default-dark": {
|
||||||
primary: "#7dd3fc",
|
primary: "#7dd3fc",
|
||||||
secondary: "#22d3ee",
|
secondary: "#22d3ee",
|
||||||
accent: "#6d28d9",
|
accent: "#6d28d9",
|
||||||
@@ -36,21 +36,124 @@ module.exports = {
|
|||||||
error: "#f1293c",
|
error: "#f1293c",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
// Red Light Theme
|
||||||
},
|
{
|
||||||
darkMode: ["class", '[data-theme="dark"]'],
|
"red-light": {
|
||||||
content: [
|
primary: "#ef4444",
|
||||||
"./app/**/*.{js,ts,jsx,tsx}",
|
secondary: "#dc2626",
|
||||||
"./pages/**/*.{js,ts,jsx,tsx}",
|
accent: "#6d28d9",
|
||||||
"./components/**/*.{js,ts,jsx,tsx}",
|
neutral: "#6b7280",
|
||||||
|
"neutral-content": "#d1d5db",
|
||||||
|
"base-100": "#ffffff",
|
||||||
|
"base-200": "#f3f4f6",
|
||||||
|
"base-content": "#0a0a0a",
|
||||||
|
info: "#a5f3fc",
|
||||||
|
success: "#22c55e",
|
||||||
|
warning: "#facc15",
|
||||||
|
error: "#dc2626",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Red Dark Theme
|
||||||
|
{
|
||||||
|
"red-dark": {
|
||||||
|
primary: "#ef4444",
|
||||||
|
secondary: "#dc2626",
|
||||||
|
accent: "#6d28d9",
|
||||||
|
neutral: "#9ca3af",
|
||||||
|
"neutral-content": "#404040",
|
||||||
|
"base-100": "#171717",
|
||||||
|
"base-200": "#262626",
|
||||||
|
"base-content": "#fafafa",
|
||||||
|
info: "#009ee4",
|
||||||
|
success: "#00b17d",
|
||||||
|
warning: "#eac700",
|
||||||
|
error: "#f1293c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Green Light Theme
|
||||||
|
{
|
||||||
|
"green-light": {
|
||||||
|
primary: "#22c55e",
|
||||||
|
secondary: "#16a34a",
|
||||||
|
accent: "#6d28d9",
|
||||||
|
neutral: "#6b7280",
|
||||||
|
"neutral-content": "#d1d5db",
|
||||||
|
"base-100": "#ffffff",
|
||||||
|
"base-200": "#f3f4f6",
|
||||||
|
"base-content": "#0a0a0a",
|
||||||
|
info: "#a5f3fc",
|
||||||
|
success: "#22c55e",
|
||||||
|
warning: "#facc15",
|
||||||
|
error: "#dc2626",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Green Dark Theme
|
||||||
|
{
|
||||||
|
"green-dark": {
|
||||||
|
primary: "#22c55e",
|
||||||
|
secondary: "#16a34a",
|
||||||
|
accent: "#6d28d9",
|
||||||
|
neutral: "#9ca3af",
|
||||||
|
"neutral-content": "#404040",
|
||||||
|
"base-100": "#171717",
|
||||||
|
"base-200": "#262626",
|
||||||
|
"base-content": "#fafafa",
|
||||||
|
info: "#009ee4",
|
||||||
|
success: "#00b17d",
|
||||||
|
warning: "#eac700",
|
||||||
|
error: "#f1293c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Orange Light Theme
|
||||||
|
{
|
||||||
|
"orange-light": {
|
||||||
|
primary: "#f97316",
|
||||||
|
secondary: "#ea580c",
|
||||||
|
accent: "#6d28d9",
|
||||||
|
neutral: "#9ca3af",
|
||||||
|
"neutral-content": "#404040",
|
||||||
|
"base-100": "#171717",
|
||||||
|
"base-200": "#262626",
|
||||||
|
"base-content": "#fafafa",
|
||||||
|
info: "#009ee4",
|
||||||
|
success: "#00b17d",
|
||||||
|
warning: "#eac700",
|
||||||
|
error: "#f1293c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Orange Dark Theme
|
||||||
|
{
|
||||||
|
"orange-dark": {
|
||||||
|
primary: "#f97316",
|
||||||
|
secondary: "#ea580c",
|
||||||
|
accent: "#6d28d9",
|
||||||
|
neutral: "#9ca3af",
|
||||||
|
"neutral-content": "#404040",
|
||||||
|
"base-100": "#171717",
|
||||||
|
"base-200": "#262626",
|
||||||
|
"base-content": "#fafafa",
|
||||||
|
info: "#009ee4",
|
||||||
|
success: "#00b17d",
|
||||||
|
warning: "#eac700",
|
||||||
|
error: "#f1293c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
darkMode: ["class", '[data-theme="dark"]'],
|
||||||
|
content: [
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx}",
|
||||||
|
|
||||||
// For the "layouts" directory
|
// For the "layouts" directory
|
||||||
"./layouts/**/*.{js,ts,jsx,tsx}",
|
"./layouts/**/*.{js,ts,jsx,tsx}",
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
require("daisyui"),
|
require("daisyui"),
|
||||||
plugin(({ addVariant }) => {
|
plugin(({ addVariant }) => {
|
||||||
addVariant("dark", '&[data-theme="dark"]');
|
addVariant("dark", '&[data-theme="dark"]');
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user