update
This commit is contained in:
137
package.json
137
package.json
@@ -8,7 +8,7 @@
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint src",
|
||||
"lint": "eslint . --fix",
|
||||
"jest": "jest",
|
||||
"jest:watch": "jest --watch",
|
||||
"test": "pnpm typecheck && pnpm lint",
|
||||
@@ -17,91 +17,90 @@
|
||||
"chromatic": "npx chromatic --project-token=180ac2186305"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mantine/core": "^7.6.2",
|
||||
"@mantine/hooks": "^7.6.2",
|
||||
"@mantine/notifications": "^7.6.2",
|
||||
"@meilisearch/instant-meilisearch": "^0.18.1",
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"@sentry/react": "^7.108.0",
|
||||
"@sentry/vite-plugin": "^2.16.0",
|
||||
"@tabler/icons-react": "^3.1.0",
|
||||
"axios": "^1.6.8",
|
||||
"dayjs": "^1.11.10",
|
||||
"i18next": "^23.10.1",
|
||||
"i18next-browser-languagedetector": "^7.2.0",
|
||||
"instantsearch.js": "^4.71.1",
|
||||
"@mantine/core": "^7.14.3",
|
||||
"@mantine/hooks": "^7.14.3",
|
||||
"@mantine/notifications": "^7.14.3",
|
||||
"@meilisearch/instant-meilisearch": "^0.22.0",
|
||||
"@reduxjs/toolkit": "^2.4.0",
|
||||
"@sentry/react": "^8.42.0",
|
||||
"@sentry/vite-plugin": "^2.22.7",
|
||||
"@tabler/icons-react": "^3.24.0",
|
||||
"axios": "^1.7.9",
|
||||
"dayjs": "^1.11.13",
|
||||
"i18next": "^24.0.5",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"instantsearch.js": "^4.75.5",
|
||||
"lodash": "^4.17.21",
|
||||
"meilisearch": "^0.40.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^14.1.0",
|
||||
"react-instantsearch": "^7.11.1",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"rehype-highlight": "^7.0.0",
|
||||
"meilisearch": "^0.46.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^15.1.3",
|
||||
"react-instantsearch": "^7.13.8",
|
||||
"react-redux": "^9.1.2",
|
||||
"react-router-dom": "^7.0.2",
|
||||
"rehype-highlight": "^7.0.1",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"rehype-stringify": "^10.0.0",
|
||||
"rehype-stringify": "^10.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-html": "^16.0.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"remark-rehype": "^11.1.0",
|
||||
"unified": "^11.0.4"
|
||||
"remark-rehype": "^11.1.1",
|
||||
"unified": "^11.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.1",
|
||||
"@mantine/form": "^7.6.2",
|
||||
"@storybook/addon-actions": "^8.0.2",
|
||||
"@storybook/addon-essentials": "^8.0.2",
|
||||
"@storybook/addon-interactions": "^8.0.2",
|
||||
"@storybook/addon-links": "^8.0.2",
|
||||
"@babel/core": "^7.26.0",
|
||||
"@mantine/form": "^7.14.3",
|
||||
"@storybook/addon-actions": "^8.4.7",
|
||||
"@storybook/addon-essentials": "^8.4.7",
|
||||
"@storybook/addon-interactions": "^8.4.7",
|
||||
"@storybook/addon-links": "^8.4.7",
|
||||
"@storybook/addons": "^7.6.17",
|
||||
"@storybook/api": "^7.6.17",
|
||||
"@storybook/builder-vite": "^8.0.2",
|
||||
"@storybook/components": "^8.0.2",
|
||||
"@storybook/core-events": "^8.0.2",
|
||||
"@storybook/react": "^8.0.2",
|
||||
"@storybook/testing-library": "^0.2.2",
|
||||
"@storybook/theming": "^8.0.2",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^14.2.2",
|
||||
"@storybook/builder-vite": "^8.4.7",
|
||||
"@storybook/components": "^8.4.7",
|
||||
"@storybook/core-events": "^8.4.7",
|
||||
"@storybook/react": "^8.4.7",
|
||||
"@storybook/theming": "^8.4.7",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.0",
|
||||
"@types/react": "^18.2.67",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
||||
"@typescript-eslint/parser": "^7.3.1",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"babel-loader": "^9.1.3",
|
||||
"chromatic": "^11.1.1",
|
||||
"eslint": "^8.57.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/lodash": "^4.17.13",
|
||||
"@types/react": "^18.3.1",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
||||
"@typescript-eslint/parser": "^8.17.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"babel-loader": "^9.2.1",
|
||||
"chromatic": "^11.20.0",
|
||||
"eslint": "^9.16.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"i18next-http-backend": "^2.5.0",
|
||||
"eslint-import-resolver-typescript": "^3.7.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"eslint-plugin-storybook": "^0.11.1",
|
||||
"i18next-http-backend": "^2.7.1",
|
||||
"install-peerdeps": "^3.0.3",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"postcss": "^8.4.37",
|
||||
"postcss-preset-mantine": "^1.13.0",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-preset-mantine": "^1.17.0",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "^3.2.5",
|
||||
"react-router": "^6.22.3",
|
||||
"prettier": "^3.4.2",
|
||||
"react-router": "^7.0.2",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"storybook": "^8.0.2",
|
||||
"storybook-dark-mode": "^4.0.1",
|
||||
"storybook-react-i18next": "3.0.1",
|
||||
"storybook": "^8.4.7",
|
||||
"storybook-dark-mode": "^4.0.2",
|
||||
"storybook-react-i18next": "3.1.7",
|
||||
"stylis-plugin-rtl": "^2.1.1",
|
||||
"ts-jest": "^29.1.2",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "^5.2.0"
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.7.2",
|
||||
"vite": "^6.0.3"
|
||||
},
|
||||
"packageManager": "pnpm@10.0.0-beta.1+sha512.629de0531b9ae9a3f8e372d014ef8f5a57906d9a48095ced54bbfbd246b4136381478032c8d13819fd1eedde8330517a799ea6756eedd9a136e36524fa3083cf"
|
||||
}
|
||||
|
||||
9570
pnpm-lock.yaml
generated
9570
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,10 @@ import {
|
||||
useSortBy,
|
||||
} from "react-instantsearch";
|
||||
|
||||
const sortItems = [{ value: "paragraph:time:desc", label: "Newest" }];
|
||||
const sortItems = [
|
||||
{ value: "paragraph:time:desc", label: "Newest" },
|
||||
{ value: "paragraph:time:asc", label: "Oldest" },
|
||||
];
|
||||
const hitsPerPageItems = [
|
||||
{ value: 20, label: "20", default: true },
|
||||
{ value: 40, label: "40" },
|
||||
|
||||
@@ -45,7 +45,6 @@ export default function MainLayout() {
|
||||
{
|
||||
finitePagination: true,
|
||||
meiliSearchParams: {
|
||||
hybrid: {},
|
||||
attributesToRetrieve: [
|
||||
"cover",
|
||||
"title",
|
||||
@@ -83,7 +82,7 @@ export default function MainLayout() {
|
||||
</Group>
|
||||
</Group>
|
||||
</AppShell.Header>
|
||||
<AppShell.Main pt={`calc(${rem(60)} + var(--mantine-spacing-md))`}>
|
||||
<AppShell.Main pt={`calc(${rem(60)} + var(--mantine-spacing-md))`} pb={rem(60)}>
|
||||
<Suspense fallback={<Loading />}>
|
||||
<Outlet />
|
||||
</Suspense>
|
||||
|
||||
@@ -1,79 +1,167 @@
|
||||
import { Container, Paper, Table, Text, TextInput, Title } from "@mantine/core";
|
||||
import { ReactNode, useContext, useEffect } from "react";
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
Container,
|
||||
Paper,
|
||||
PasswordInput,
|
||||
rem,
|
||||
Switch,
|
||||
Table,
|
||||
Text,
|
||||
TextInput,
|
||||
Title
|
||||
} from "@mantine/core";
|
||||
import { useForm } from "@mantine/form";
|
||||
import { ReactNode, useContext, useEffect, useState } from "react";
|
||||
|
||||
import { TitleContext } from "@/component/Header/Header";
|
||||
import { ThemeSetting } from "@/component/Settings/Theme";
|
||||
import { disableSentry } from "@/sentry";
|
||||
import store from "@/store";
|
||||
import { useOptionsState } from "@/store/module/options";
|
||||
import {
|
||||
setMeilisearchToken,
|
||||
setMeilisearchUrl,
|
||||
setS3Url,
|
||||
} from "@/store/reducer/options";
|
||||
import { setMeilisearchToken, setMeilisearchUrl, setS3Url } from "@/store/reducer/options";
|
||||
import { useLocalStorage } from "@mantine/hooks";
|
||||
import { IconBrandGithub, IconMail } from "@tabler/icons-react";
|
||||
import { Meilisearch } from "meilisearch";
|
||||
|
||||
interface SettingItem {
|
||||
title: string;
|
||||
description: string;
|
||||
value: ReactNode;
|
||||
title?: ReactNode;
|
||||
description?: ReactNode;
|
||||
value?: ReactNode;
|
||||
}
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [_, setTitle] = useContext(TitleContext);
|
||||
const { state: options } = useOptionsState();
|
||||
const [sentryDisabled, setSentryDisabled] = useLocalStorage({
|
||||
key: "sentryDisabled",
|
||||
defaultValue: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTitle("Settings");
|
||||
}, [setTitle]);
|
||||
|
||||
const settings: SettingItem[] = [
|
||||
{
|
||||
title: "Theme",
|
||||
description: "Change the theme of your UI",
|
||||
value: <ThemeSetting />,
|
||||
},
|
||||
{
|
||||
title: "Minio URL",
|
||||
description: "The URL of your Minio instance",
|
||||
value: (
|
||||
<TextInput
|
||||
value={options.s3Url}
|
||||
onChange={(e) => {
|
||||
store.dispatch(setS3Url(e.currentTarget.value));
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Meilisearch URL",
|
||||
description: "The URL of your Meilisearch instance",
|
||||
value: (
|
||||
<TextInput
|
||||
value={options.meilisearchUrl}
|
||||
onChange={(e) => {
|
||||
store.dispatch(setMeilisearchUrl(e.currentTarget.value));
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Meilisearch Token",
|
||||
description: "The token of your Meilisearch instance",
|
||||
value: (
|
||||
<TextInput
|
||||
value={options.meilisearchToken}
|
||||
onChange={(e) => {
|
||||
store.dispatch(setMeilisearchToken(e.currentTarget.value));
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Made By",
|
||||
description: "Yoshino-s",
|
||||
value: (
|
||||
<a href="https://github.com/yoshino-s">https://github.com/yoshino-s</a>
|
||||
),
|
||||
},
|
||||
const form = useForm({
|
||||
initialValues: options,
|
||||
validateInputOnBlur: true,
|
||||
validateInputOnChange: true,
|
||||
});
|
||||
|
||||
const [meilisearchVersion, setMeilisearchVersion] = useState<string | null>(null);
|
||||
|
||||
const onSubmit = async (v: typeof options) => {
|
||||
try {
|
||||
new URL(v.s3Url, location.origin);
|
||||
form.clearFieldError("s3Url");
|
||||
} catch (e) {
|
||||
form.setFieldError("s3Url", "Invalid Minio URL");
|
||||
}
|
||||
|
||||
try {
|
||||
const client = new Meilisearch({
|
||||
host: v.meilisearchUrl,
|
||||
apiKey: v.meilisearchToken,
|
||||
});
|
||||
const version = await client.getVersion();
|
||||
setMeilisearchVersion(version.pkgVersion);
|
||||
form.clearFieldError("meilisearchUrl");
|
||||
form.clearFieldError("meilisearchToken");
|
||||
} catch (e) {
|
||||
form.setFieldError("meilisearchUrl", "Invalid Meilisearch URL");
|
||||
form.setFieldError("meilisearchToken", "Invalid Meilisearch Token");
|
||||
}
|
||||
|
||||
if (form.errors.length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (v.s3Url !== options.s3Url) {
|
||||
store.dispatch(setS3Url(v.s3Url));
|
||||
}
|
||||
if (v.meilisearchUrl !== options.meilisearchUrl) {
|
||||
store.dispatch(setMeilisearchUrl(v.meilisearchUrl));
|
||||
}
|
||||
if (v.meilisearchToken !== options.meilisearchToken) {
|
||||
store.dispatch(setMeilisearchToken(v.meilisearchToken));
|
||||
}
|
||||
};
|
||||
|
||||
const settings: SettingItem[][] = [
|
||||
[
|
||||
{
|
||||
title: "Theme",
|
||||
description: "Change the theme of your UI",
|
||||
value: <ThemeSetting />,
|
||||
},
|
||||
{
|
||||
title: "Disable Sentry",
|
||||
description: "Disable Sentry error reporting (not recommended)",
|
||||
value: (
|
||||
<div>
|
||||
<Switch
|
||||
size="md"
|
||||
checked={sentryDisabled}
|
||||
onChange={(value) => {
|
||||
const v = value.currentTarget.checked;
|
||||
setSentryDisabled(v);
|
||||
disableSentry(v);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
title: "Minio URL",
|
||||
description: "The URL of your Minio instance",
|
||||
value: <TextInput {...form.getInputProps("s3Url")} required />,
|
||||
},
|
||||
{
|
||||
title: "Meilisearch URL",
|
||||
description: "The URL of your Meilisearch instance",
|
||||
value: <TextInput {...form.getInputProps("meilisearchUrl")} required />,
|
||||
},
|
||||
{
|
||||
title: "Meilisearch Token",
|
||||
description: "The token of your Meilisearch instance",
|
||||
value: (
|
||||
<PasswordInput {...form.getInputProps("meilisearchToken")} required />
|
||||
),
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<Button type="submit">Test And Save</Button>
|
||||
),
|
||||
value: (
|
||||
meilisearchVersion ? (
|
||||
<span style={{
|
||||
color: "var(--mantine-color-green-6)",
|
||||
}}>Meilisearch V{meilisearchVersion}</span>
|
||||
) : null
|
||||
)
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
title: "Made With ❤️ By",
|
||||
description: (
|
||||
<a href="https://github.com/yoshino-s">https://github.com/yoshino-s</a>
|
||||
),
|
||||
value: (
|
||||
<ActionIcon.Group>
|
||||
<ActionIcon variant="default" size="lg" aria-label="Gallery" component="a" href="https://github.com/yoshino-s" target="_blank">
|
||||
<IconBrandGithub style={{ width: rem(20) }} stroke={1.5} />
|
||||
</ActionIcon>
|
||||
|
||||
<ActionIcon variant="default" size="lg" aria-label="Settings" component="a" href="mailto:yoshino.prog@gmail.com">
|
||||
<IconMail style={{ width: rem(20) }} stroke={1.5} />
|
||||
</ActionIcon>
|
||||
</ActionIcon.Group>
|
||||
)
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -82,31 +170,33 @@ export default function SettingsPage() {
|
||||
Settings
|
||||
</Title>
|
||||
Customize the look and feel of your Coder deployment.
|
||||
<Paper my="xl" radius="md" withBorder style={{ overflow: "hidden" }}>
|
||||
<Table verticalSpacing="lg" striped>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Name</Table.Th>
|
||||
<Table.Th>Value</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{settings.map((setting) => (
|
||||
<Table.Tr key={`${setting.title}`}>
|
||||
<Table.Td>
|
||||
<Text size="md" fw={500}>
|
||||
{setting.title}
|
||||
</Text>
|
||||
<Text c="dimmed" size="sm">
|
||||
{setting.description}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>{setting.value}</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
||||
<form onSubmit={form.onSubmit(onSubmit)}>
|
||||
{
|
||||
settings.map((settingItem, index) => (
|
||||
<Paper my="xl" radius="md" withBorder style={{ overflow: "hidden" }} key={index}>
|
||||
<Table verticalSpacing="lg" striped>
|
||||
<Table.Tbody>
|
||||
{settingItem.map((setting) => (
|
||||
<Table.Tr key={`${setting.title}`}>
|
||||
<Table.Td>
|
||||
<Text size="md" fw={500}>
|
||||
{setting.title}
|
||||
</Text>
|
||||
<Text c="dimmed" size="sm">
|
||||
{setting.description}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>{setting.value}</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
))
|
||||
}
|
||||
</form>
|
||||
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Sentry from "@sentry/react";
|
||||
|
||||
Sentry.init({
|
||||
export const client = Sentry.init({
|
||||
dsn: "https://fee4fec58516c464f60613b40b5d3a7d@sentry.yoshino-s.xyz/2",
|
||||
integrations: [
|
||||
Sentry.browserProfilingIntegration(),
|
||||
@@ -19,4 +19,13 @@ Sentry.init({
|
||||
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
|
||||
profilesSampleRate: 1.0, // Capture 100% of the profiles
|
||||
enabled: localStorage.getItem("sentryDisabled") !== "true",
|
||||
});
|
||||
|
||||
export function disableSentry(disable: boolean) {
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
client.getOptions().enabled = !disable;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,16 @@ import optionsReducer from "./reducer/options";
|
||||
const localStorageMiddleware: Middleware = ({ getState }) => {
|
||||
return (next) => (action) => {
|
||||
const result = next(action);
|
||||
console.log(result);
|
||||
localStorage.setItem("applicationState", JSON.stringify(getState()));
|
||||
const state = getState();
|
||||
localStorage.setItem("applicationState", JSON.stringify(state));
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
const reHydrateStore = () => {
|
||||
if (localStorage.getItem("applicationState") !== null) {
|
||||
return JSON.parse(localStorage.getItem("applicationState") ?? "{}"); // re-hydrate the store
|
||||
const state: any = JSON.parse(localStorage.getItem("applicationState") ?? "{}");
|
||||
return state;// re-hydrate the store
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user