mirror of
https://github.com/dsoaress/link-free.git
synced 2026-08-17 06:56:04 +01:00
feat(wip): add dashboard
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@stitches/react": "^1.2.6",
|
"@stitches/react": "^1.2.6",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"next": "12.0.7",
|
"next": "12.0.7",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "17.0.2",
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "15.0.0",
|
"@commitlint/cli": "15.0.0",
|
||||||
"@commitlint/config-conventional": "15.0.0",
|
"@commitlint/config-conventional": "15.0.0",
|
||||||
|
"@types/lodash": "^4.14.178",
|
||||||
"@types/node": "16.11.12",
|
"@types/node": "16.11.12",
|
||||||
"@types/react": "17.0.37",
|
"@types/react": "17.0.37",
|
||||||
"eslint": "8.4.1",
|
"eslint": "8.4.1",
|
||||||
|
|||||||
21
src/components/Dash/DescriptionInput/index.tsx
Normal file
21
src/components/Dash/DescriptionInput/index.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { useData } from '../../../contexts/DataContext'
|
||||||
|
|
||||||
|
export function DescriptionInput() {
|
||||||
|
const { data, setData } = useData()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
style={{ height: '200px', width: '100%' }}
|
||||||
|
value={data.settings.description}
|
||||||
|
onChange={e =>
|
||||||
|
setData({
|
||||||
|
...data,
|
||||||
|
settings: {
|
||||||
|
...data.settings,
|
||||||
|
description: e.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
20
src/components/Dash/NameInput/index.tsx
Normal file
20
src/components/Dash/NameInput/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useData } from '../../../contexts/DataContext'
|
||||||
|
|
||||||
|
export function NameInput() {
|
||||||
|
const { data, setData } = useData()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
value={data.settings.name}
|
||||||
|
onChange={e =>
|
||||||
|
setData({
|
||||||
|
...data,
|
||||||
|
settings: {
|
||||||
|
...data.settings,
|
||||||
|
name: e.target.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
108
src/components/Dash/index.tsx
Normal file
108
src/components/Dash/index.tsx
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { isEqual } from 'lodash'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { useData } from '../../contexts/DataContext'
|
||||||
|
import { theme } from '../../styles/stitches.config'
|
||||||
|
import {
|
||||||
|
getLocalStorageData,
|
||||||
|
removeLocalStorageData,
|
||||||
|
setLocalStorageData
|
||||||
|
} from '../../utils/localStorage'
|
||||||
|
import { Home } from '../Home'
|
||||||
|
import { DescriptionInput } from './DescriptionInput'
|
||||||
|
import { NameInput } from './NameInput'
|
||||||
|
import { Content, Preview, Wrapper } from './styles'
|
||||||
|
|
||||||
|
export function Dash() {
|
||||||
|
const { initialData, data, setData } = useData()
|
||||||
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const storageData = getLocalStorageData()
|
||||||
|
|
||||||
|
if (storageData && isLoading) {
|
||||||
|
setData(storageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false)
|
||||||
|
}, [isLoading, setData])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isEqual(initialData, data)) {
|
||||||
|
setHasUnsavedChanges(true)
|
||||||
|
setLocalStorageData(data)
|
||||||
|
} else {
|
||||||
|
setHasUnsavedChanges(false)
|
||||||
|
removeLocalStorageData()
|
||||||
|
}
|
||||||
|
}, [data, initialData])
|
||||||
|
|
||||||
|
const handleChangeBackground = (background: string) => {
|
||||||
|
setData({
|
||||||
|
...data,
|
||||||
|
settings: { ...data.settings, colors: { ...data.settings.colors, background } }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChangeButtonsSchema = (schema: string) => {
|
||||||
|
setData({ ...data, settings: { ...data.settings, buttonsSchema: schema } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChangeOutline = (outline: boolean) => {
|
||||||
|
setData({ ...data, settings: { ...data.settings, outline } })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChangeFont = (font: string) => {
|
||||||
|
setData({ ...data, settings: { ...data.settings, font } })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <div>Loading...</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<Content>
|
||||||
|
<h1>Dash {hasUnsavedChanges && '- has unsaved changes'}</h1>
|
||||||
|
<NameInput />
|
||||||
|
<DescriptionInput />
|
||||||
|
|
||||||
|
<select value={data.settings.font} onChange={e => handleChangeFont(e.target.value)}>
|
||||||
|
<option value="square">Baloo</option>
|
||||||
|
<option value="montserrat">Montserrat</option>
|
||||||
|
<option value="roboto">Roboto</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select
|
||||||
|
value={data.settings.colors.background}
|
||||||
|
onChange={e => handleChangeBackground(e.target.value)}
|
||||||
|
>
|
||||||
|
{Object.keys(theme.colors).map(color => (
|
||||||
|
<option key={color} value={color}>
|
||||||
|
{color}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select
|
||||||
|
value={data.settings.buttonsSchema}
|
||||||
|
onChange={e => handleChangeButtonsSchema(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="square">Square</option>
|
||||||
|
<option value="rounded">Rounded</option>
|
||||||
|
<option value="pill">Pill</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
defaultChecked={data.settings.outline}
|
||||||
|
onChange={() => handleChangeOutline(!data.settings.outline)}
|
||||||
|
/>
|
||||||
|
</Content>
|
||||||
|
<Preview>
|
||||||
|
<Home />
|
||||||
|
</Preview>
|
||||||
|
</Wrapper>
|
||||||
|
)
|
||||||
|
}
|
||||||
26
src/components/Dash/styles.ts
Normal file
26
src/components/Dash/styles.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { styled, theme } from '../../styles/stitches.config'
|
||||||
|
|
||||||
|
export const Wrapper = styled('main', {
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1fr 1fr'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const Content = styled('div', {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '1rem',
|
||||||
|
fontFamily: theme.fonts.roboto
|
||||||
|
})
|
||||||
|
|
||||||
|
export const Preview = styled('div', {
|
||||||
|
position: 'relative',
|
||||||
|
|
||||||
|
'&::after': {
|
||||||
|
content: '',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -13,7 +13,8 @@ export const Home = () => {
|
|||||||
<Wrapper
|
<Wrapper
|
||||||
css={{
|
css={{
|
||||||
background: theme.colors[colors.background as keyof typeof theme.colors],
|
background: theme.colors[colors.background as keyof typeof theme.colors],
|
||||||
color: theme.colors[colors.texts as keyof typeof theme.colors]
|
color: theme.colors[colors.texts as keyof typeof theme.colors],
|
||||||
|
fontFamily: theme.fonts[font as keyof typeof theme.fonts]
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Content>
|
<Content>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import initialData from '../../data/data.json'
|
|||||||
type DataType = typeof initialData
|
type DataType = typeof initialData
|
||||||
|
|
||||||
type DataContextType = {
|
type DataContextType = {
|
||||||
|
initialData: DataType
|
||||||
data: DataType
|
data: DataType
|
||||||
setData: Dispatch<SetStateAction<DataType>>
|
setData: Dispatch<SetStateAction<DataType>>
|
||||||
}
|
}
|
||||||
@@ -18,7 +19,9 @@ const DataContext = createContext({} as DataContextType)
|
|||||||
export const DataProvider = ({ children }: DataProviderProps) => {
|
export const DataProvider = ({ children }: DataProviderProps) => {
|
||||||
const [data, setData] = useState<DataType>(initialData)
|
const [data, setData] = useState<DataType>(initialData)
|
||||||
|
|
||||||
return <DataContext.Provider value={{ data, setData }}>{children}</DataContext.Provider>
|
return (
|
||||||
|
<DataContext.Provider value={{ initialData, data, setData }}>{children}</DataContext.Provider>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useData = () => useContext(DataContext)
|
export const useData = () => useContext(DataContext)
|
||||||
|
|||||||
5
src/pages/dash.tsx
Normal file
5
src/pages/dash.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { Dash } from '../components/Dash'
|
||||||
|
|
||||||
|
export default function DashPage() {
|
||||||
|
return <Dash />
|
||||||
|
}
|
||||||
14
src/utils/localStorage.ts
Normal file
14
src/utils/localStorage.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const KEY = 'link-free-data'
|
||||||
|
|
||||||
|
export function getLocalStorageData() {
|
||||||
|
const data = process.browser && localStorage.getItem(KEY)
|
||||||
|
return data ? JSON.parse(data) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLocalStorageData(data = {}) {
|
||||||
|
localStorage.setItem(KEY, JSON.stringify(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeLocalStorageData() {
|
||||||
|
localStorage.removeItem(KEY)
|
||||||
|
}
|
||||||
@@ -406,6 +406,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||||
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
|
||||||
|
|
||||||
|
"@types/lodash@^4.14.178":
|
||||||
|
version "4.14.178"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8"
|
||||||
|
integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==
|
||||||
|
|
||||||
"@types/minimatch@^3.0.3":
|
"@types/minimatch@^3.0.3":
|
||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
|
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
|
||||||
@@ -2506,7 +2511,7 @@ lodash.sortby@^4.7.0:
|
|||||||
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
|
||||||
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
|
||||||
|
|
||||||
lodash@^4.17.15, lodash@^4.17.19:
|
lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|||||||
Reference in New Issue
Block a user