feat: custom branding (#112)

* add first concept

* remove setup status

* split config page in multiple components

* add custom branding docs

* add test email button

* fix invalid email from header

* add migration

* mount images to host

* update docs

* remove unused endpoint

* run formatter
This commit is contained in:
Elias Schneider
2023-03-04 23:29:00 +01:00
committed by GitHub
parent f9840505b8
commit fddad3ef70
66 changed files with 908 additions and 623 deletions

View File

@@ -1,18 +0,0 @@
import { Space, Title } from "@mantine/core";
import AdminConfigTable from "../../components/admin/configuration/AdminConfigTable";
import Meta from "../../components/Meta";
const AdminConfig = () => {
return (
<>
<Meta title="Configuration" />
<Title mb={30} order={3}>
Configuration
</Title>
<AdminConfigTable />
<Space h="xl" />
</>
);
};
export default AdminConfig;

View File

@@ -0,0 +1,148 @@
import {
AppShell,
Box,
Button,
Container,
Group,
Stack,
Text,
Title,
useMantineTheme,
} from "@mantine/core";
import { useMediaQuery } from "@mantine/hooks";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import AdminConfigInput from "../../../components/admin/configuration/AdminConfigInput";
import ConfigurationHeader from "../../../components/admin/configuration/ConfigurationHeader";
import ConfigurationNavBar from "../../../components/admin/configuration/ConfigurationNavBar";
import TestEmailButton from "../../../components/admin/configuration/TestEmailButton";
import CenterLoader from "../../../components/core/CenterLoader";
import Meta from "../../../components/Meta";
import useConfig from "../../../hooks/config.hook";
import configService from "../../../services/config.service";
import { AdminConfig, UpdateConfig } from "../../../types/config.type";
import {
capitalizeFirstLetter,
configVariableToFriendlyName,
} from "../../../utils/string.util";
import toast from "../../../utils/toast.util";
export default function AppShellDemo() {
const theme = useMantineTheme();
const router = useRouter();
const [isMobileNavBarOpened, setIsMobileNavBarOpened] = useState(false);
const isMobile = useMediaQuery("(max-width: 560px)");
const config = useConfig();
const categoryId = router.query.category as string;
const [configVariables, setConfigVariables] = useState<AdminConfig[]>();
const [updatedConfigVariables, setUpdatedConfigVariables] = useState<
UpdateConfig[]
>([]);
const saveConfigVariables = async () => {
await configService
.updateMany(updatedConfigVariables)
.then(() => {
setUpdatedConfigVariables([]);
toast.success("Configurations updated successfully");
})
.catch(toast.axiosError);
config.refresh();
};
const updateConfigVariable = (configVariable: UpdateConfig) => {
const index = updatedConfigVariables.findIndex(
(item) => item.key === configVariable.key
);
if (index > -1) {
updatedConfigVariables[index] = configVariable;
} else {
setUpdatedConfigVariables([...updatedConfigVariables, configVariable]);
}
};
useEffect(() => {
configService.getByCategory(categoryId).then((configVariables) => {
setConfigVariables(configVariables);
});
}, [categoryId]);
return (
<>
<Meta title="Configuration" />
<AppShell
styles={{
main: {
background:
theme.colorScheme === "dark"
? theme.colors.dark[8]
: theme.colors.gray[0],
},
}}
navbar={
<ConfigurationNavBar
categoryId={categoryId}
isMobileNavBarOpened={isMobileNavBarOpened}
setIsMobileNavBarOpened={setIsMobileNavBarOpened}
/>
}
header={
<ConfigurationHeader
isMobileNavBarOpened={isMobileNavBarOpened}
setIsMobileNavBarOpened={setIsMobileNavBarOpened}
/>
}
>
<Container size="lg">
{!configVariables ? (
<CenterLoader />
) : (
<>
<Stack>
<Title mb="md" order={3}>
{capitalizeFirstLetter(categoryId)}
</Title>
{configVariables.map((configVariable) => (
<Group key={configVariable.key} position="apart">
<Stack
style={{ maxWidth: isMobile ? "100%" : "40%" }}
spacing={0}
>
<Title order={6}>
{configVariableToFriendlyName(configVariable.name)}
</Title>
<Text color="dimmed" size="sm" mb="xs">
{configVariable.description}
</Text>
</Stack>
<Stack></Stack>
<Box style={{ width: isMobile ? "100%" : "50%" }}>
<AdminConfigInput
key={configVariable.key}
configVariable={configVariable}
updateConfigVariable={updateConfigVariable}
/>
</Box>
</Group>
))}
</Stack>
<Group mt="lg" position="right">
{categoryId == "smtp" && (
<TestEmailButton
configVariablesChanged={updatedConfigVariables.length != 0}
saveConfigVariables={saveConfigVariables}
/>
)}
<Button onClick={saveConfigVariables}>Save</Button>
</Group>
</>
)}
</Container>
</AppShell>
</>
);
}

View File

@@ -0,0 +1,15 @@
export function getServerSideProps() {
return {
redirect: {
permanent: false,
destination: "/admin/config/general",
},
props: {},
};
}
const Config = () => {
return null;
};
export default Config;

View File

@@ -0,0 +1,59 @@
import {
Anchor,
Button,
Center,
Container,
Stack,
Text,
Title,
} from "@mantine/core";
import Link from "next/link";
import Logo from "../../components/Logo";
import Meta from "../../components/Meta";
const Intro = () => {
return (
<>
<Meta title="Intro" />
<Container size="xs">
<Stack>
<Center>
<Logo height={80} width={80} />
</Center>
<Center>
<Title order={2}>Welcome to Pingvin Share</Title>
</Center>
<Text>
If you enjoy Pingvin Share please it on{" "}
<Anchor
target="_blank"
href="https://github.com/stonith404/pingvin-share"
>
GitHub
</Anchor>{" "}
or{" "}
<Anchor
target="_blank"
href="https://github.com/sponsors/stonith404"
>
buy me a coffee
</Anchor>{" "}
if you want to support my work.
</Text>
<Text>Enough talked, have fun with Pingvin Share!</Text>
<Text mt="lg">How to you want to continue?</Text>
<Stack>
<Button href="/admin/config" component={Link}>
Customize configuration
</Button>
<Button href="/" component={Link} variant="light">
Explore Pingvin Share
</Button>
</Stack>
</Stack>
</Container>
</>
);
};
export default Intro;

View File

@@ -1,23 +0,0 @@
import { Box, Stack, Text, Title } from "@mantine/core";
import AdminConfigTable from "../../components/admin/configuration/AdminConfigTable";
import Logo from "../../components/Logo";
import Meta from "../../components/Meta";
const Setup = () => {
return (
<>
<Meta title="Setup" />
<Stack align="center">
<Logo height={80} width={80} />
<Title order={2}>Welcome to Pingvin Share</Title>
<Text>Let's customize Pingvin Share for you! </Text>
<Box style={{ width: "100%" }}>
<AdminConfigTable />
</Box>
</Stack>
</>
);
};
export default Setup;

View File

@@ -58,7 +58,7 @@ const Users = () => {
</Title>
<Button
onClick={() =>
showCreateUserModal(modals, config.get("SMTP_ENABLED"), getUsers)
showCreateUserModal(modals, config.get("smtp.enabled"), getUsers)
}
leftIcon={<TbPlus size={20} />}
>