Add footer
This commit is contained in:
parent
ac13e77dc4
commit
ee667fd759
6 changed files with 4311 additions and 51 deletions
|
@ -6,6 +6,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@hookform/resolvers": "^3.10.0",
|
"@hookform/resolvers": "^3.10.0",
|
||||||
|
"@icons-pack/react-simple-icons": "^11.2.0",
|
||||||
"@mantine/core": "^7.16.1",
|
"@mantine/core": "^7.16.1",
|
||||||
"@mantine/form": "^7.16.1",
|
"@mantine/form": "^7.16.1",
|
||||||
"@mantine/hooks": "^7.16.1",
|
"@mantine/hooks": "^7.16.1",
|
||||||
|
@ -198,6 +199,8 @@
|
||||||
|
|
||||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.1", "", {}, "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA=="],
|
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.1", "", {}, "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA=="],
|
||||||
|
|
||||||
|
"@icons-pack/react-simple-icons": ["@icons-pack/react-simple-icons@11.2.0", "", { "peerDependencies": { "react": "^16.13 || ^17 || ^18 || ^19" } }, "sha512-jCJ+1Fe0yiBQGYSfhx8QGU/9o27t8J4Hw3mxHEI9vohRltLSi5CaPzO2fCQcMNeTrAUAm4j+yaDuAutskiKRjA=="],
|
||||||
|
|
||||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||||
|
|
||||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||||
|
|
4162
frontend/package-lock.json
generated
Normal file
4162
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@hookform/resolvers": "^3.10.0",
|
"@hookform/resolvers": "^3.10.0",
|
||||||
|
"@icons-pack/react-simple-icons": "^11.2.0",
|
||||||
"@mantine/core": "^7.16.1",
|
"@mantine/core": "^7.16.1",
|
||||||
"@mantine/form": "^7.16.1",
|
"@mantine/form": "^7.16.1",
|
||||||
"@mantine/hooks": "^7.16.1",
|
"@mantine/hooks": "^7.16.1",
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { ThemeProvider } from "@/components/theme-provider"
|
||||||
import { LinkForm } from './components/LinkForm'
|
import { LinkForm } from './components/LinkForm'
|
||||||
import { LinkList } from './components/LinkList'
|
import { LinkList } from './components/LinkList'
|
||||||
import { AuthForms } from './components/AuthForms'
|
import { AuthForms } from './components/AuthForms'
|
||||||
|
import { Footer } from './components/Footer'
|
||||||
import { AuthProvider, useAuth } from './context/AuthContext'
|
import { AuthProvider, useAuth } from './context/AuthContext'
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Toaster } from './components/ui/toaster'
|
import { Toaster } from './components/ui/toaster'
|
||||||
|
@ -9,61 +10,61 @@ import { ModeToggle } from './components/mode-toggle'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
function AppContent() {
|
function AppContent() {
|
||||||
const { user, logout } = useAuth()
|
const { user, logout } = useAuth()
|
||||||
const [refreshCounter, setRefreshCounter] = useState(0)
|
const [refreshCounter, setRefreshCounter] = useState(0)
|
||||||
|
|
||||||
|
const handleLinkCreated = () => {
|
||||||
|
setRefreshCounter(prev => prev + 1)
|
||||||
|
}
|
||||||
|
|
||||||
const handleLinkCreated = () => {
|
return (
|
||||||
setRefreshCounter(prev => prev + 1)
|
<div className="min-h-screen bg-background flex flex-col">
|
||||||
}
|
<header className="border-b">
|
||||||
|
<div className="container max-w-6xl mx-auto flex h-16 items-center justify-between px-4">
|
||||||
return (
|
<h1 className="text-2xl font-bold">SimpleLink</h1>
|
||||||
<div className="min-h-screen bg-background flex flex-col">
|
<div className="flex items-center space-x-2 sm:space-x-4">
|
||||||
<header className="border-b">
|
{user ? (
|
||||||
<div className="container max-w-6xl mx-auto flex h-16 items-center justify-between px-4">
|
<>
|
||||||
<h1 className="text-2xl font-bold">SimpleLink</h1>
|
<span className="text-sm text-muted-foreground hidden sm:inline">Welcome, {user.email}</span>
|
||||||
<div className="flex items-center gap-4">
|
<Button variant="outline" size="sm" onClick={logout}>
|
||||||
{user ? (
|
Logout
|
||||||
<>
|
</Button>
|
||||||
<span className="text-sm text-muted-foreground">Welcome, {user.email}</span>
|
</>
|
||||||
<Button variant="outline" size="sm" onClick={logout}>
|
) : (
|
||||||
Logout
|
<span className="text-sm text-muted-foreground mr-2">A link shortening service</span>
|
||||||
</Button>
|
)}
|
||||||
</>
|
<ModeToggle />
|
||||||
) : (
|
</div>
|
||||||
<span className="text-sm text-muted-foreground">A link shortening and tracking service</span>
|
</div>
|
||||||
)}
|
</header>
|
||||||
<ModeToggle />
|
<main className="flex-1 flex flex-col">
|
||||||
</div>
|
<div className="container max-w-6xl mx-auto px-4 py-8 flex-1 flex flex-col">
|
||||||
</div>
|
<div className="space-y-8 flex-1 flex flex-col justify-center">
|
||||||
</header>
|
{user ? (
|
||||||
|
<>
|
||||||
<main className="flex-1 flex flex-col">
|
<LinkForm onSuccess={handleLinkCreated} />
|
||||||
<div className="container max-w-6xl mx-auto px-4 py-8 flex-1 flex flex-col">
|
<LinkList refresh={refreshCounter} />
|
||||||
<div className="space-y-8 flex-1 flex flex-col justify-center">
|
</>
|
||||||
{user ? (
|
) : (
|
||||||
<>
|
<AuthForms />
|
||||||
<LinkForm onSuccess={handleLinkCreated} />
|
)}
|
||||||
<LinkList refresh={refreshCounter} />
|
</div>
|
||||||
</>
|
</div>
|
||||||
) : (
|
</main>
|
||||||
<AuthForms />
|
<Footer />
|
||||||
)}
|
</div>
|
||||||
</div>
|
)
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<AppContent />
|
<AppContent />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App
|
54
frontend/src/components/Footer.tsx
Normal file
54
frontend/src/components/Footer.tsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { SiGithub, SiBluesky } from "@icons-pack/react-simple-icons"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { PrivacyModal } from './PrivacyModal'
|
||||||
|
|
||||||
|
export function Footer() {
|
||||||
|
const [privacyModalOpen, setPrivacyModalOpen] = useState(false)
|
||||||
|
|
||||||
|
const handlePrivacyModalOpen = () => {
|
||||||
|
setPrivacyModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePrivacyModalClose = () => {
|
||||||
|
setPrivacyModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<footer className="border-t">
|
||||||
|
<div className="container max-w-6xl mx-auto flex h-14 items-center justify-between px-4">
|
||||||
|
<p className="text-sm text-muted-foreground">Created by waveringana</p>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<nav className="flex items-center space-x-4">
|
||||||
|
<a
|
||||||
|
onClick={handlePrivacyModalOpen}
|
||||||
|
href="#"
|
||||||
|
>
|
||||||
|
Privacy
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<a href="https://l.nekomimi.pet/github?source=shortener" target="_blank" rel="noopener noreferrer">
|
||||||
|
<SiGithub className="h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
<span className="sr-only">GitHub</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button variant="ghost" size="icon">
|
||||||
|
<a href="https://l.nekomimi.pet/twitter?source=shortener" target="_blank" rel="noopener noreferrer">
|
||||||
|
<SiBluesky className="h-4 w-4" />
|
||||||
|
</a>
|
||||||
|
<span className="sr-only">Twitter</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<PrivacyModal
|
||||||
|
isOpen={privacyModalOpen}
|
||||||
|
onClose={handlePrivacyModalClose}
|
||||||
|
/>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
39
frontend/src/components/PrivacyModal.tsx
Normal file
39
frontend/src/components/PrivacyModal.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog"
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
|
||||||
|
interface PrivacyModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PrivacyModal({ isOpen, onClose }: PrivacyModalProps) {
|
||||||
|
return (
|
||||||
|
<Dialog open={isOpen}>
|
||||||
|
<DialogContent className="max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Privacy Policy</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Simplelink's data collection and usage policies
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
<p>Simplelink shortens URLs and tracks only two pieces of information: the time each link is clicked and the source of the link through a ?source= query tag. We do not collect any personal information such as IP addresses or any other data.</p>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button variant="outline" onClick={onClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue