Initial commit

This commit is contained in:
LenaGmbh
2026-07-28 21:58:43 +02:00
commit 2129f1f7d0
20 changed files with 3443 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+8
View File
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "oxc"],
"rules": {
"react/rules-of-hooks": "error",
"react/only-export-components": ["warn", { "allowConstantExport": true }]
}
}
+32
View File
@@ -0,0 +1,32 @@
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some Oxlint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the Oxlint configuration
If you are developing a production application, we recommend enabling type-aware lint rules by installing `oxlint-tsgolint` and editing `.oxlintrc.json`:
```json
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "oxc"],
"options": {
"typeAware": true
},
"rules": {
"react/rules-of-hooks": "error",
"react/only-export-components": ["warn", { "allowConstantExport": true }]
}
}
```
See the [Oxlint rules documentation](https://oxc.rs/docs/guide/usage/linter/rules) for the full list of rules and categories.
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en" class="dark" data-theme="dark">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>journalsite</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+2540
View File
File diff suppressed because it is too large Load Diff
+30
View File
@@ -0,0 +1,30 @@
{
"name": "journalsite",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "oxlint",
"preview": "vite preview"
},
"dependencies": {
"@heroui/react": "^3.2.2",
"@heroui/styles": "^3.2.2",
"react": "^19.2.7",
"react-dom": "^19.2.7"
},
"devDependencies": {
"@gravity-ui/icons": "^2.21.0",
"@tailwindcss/vite": "^4.3.3",
"@types/node": "^24.13.2",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.3",
"oxlint": "^1.71.0",
"tailwindcss": "^4.3.3",
"typescript": "~6.0.2",
"vite": "^8.1.1"
}
}
+55
View File
@@ -0,0 +1,55 @@
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800&display=swap');
@import "tailwindcss";
@import "@heroui/styles";
:root {
--text: #6b6375;
--text-h: #08060d;
--bg: #fff;
--border: #e5e4e7;
--code-bg: #f4f3ec;
--black-background: #060607;
--default-background: #111111;
--card-background: #18181B;
--border-color: #3F3F46;
--border-onHover-Color: #71717A;
--accent: #aa3bff;
--accent-bg: rgba(170, 59, 255, 0.1);
--accent-border: rgba(170, 59, 255, 0.5);
--social-bg: rgba(244, 243, 236, 0.5);
--shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;
--button-text-color-secondary: #D0A6FE;
--sans: 'Nunito', system-ui, 'Segoe UI', Roboto, sans-serif;
--heading: 'Nunito', system-ui, 'Segoe UI', Roboto, sans-serif;
--mono: ui-monospace, Consolas, monospace;
font: 18px/145% var(--sans);
letter-spacing: 0.18px;
color-scheme: light dark;
color: var(--text);
background: var(--default-background);
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@media (max-width: 1024px) {
font-size: 16px;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
}
+74
View File
@@ -0,0 +1,74 @@
import { Button, Card, Form, Input, Label, Link } from "@heroui/react";
import './login.css'
import * as React from "react";
interface LoginProps {
onLogin?: () => void;
}
export function Login({ onLogin }: LoginProps) {
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onLogin?.();
}
return (
<div className="relative flex min-h-dvh flex-col overflow-hidden">
<main className="container z-10 mx-auto flex flex-1 items-center justify-center">
<Card className="w-full max-w-[420px] bg-[var(--card-background)]">
<Card.Header className="flex flex-col items-center pb-2">
<Card.Title className="text-2xl font-bold font-[Nunito] text-white">Login</Card.Title>
<Card.Description>Welcome back</Card.Description>
</Card.Header>
<Form onSubmit={onSubmit}>
<Card.Content className="w-full">
<div className="flex flex-col gap-2 w-full">
<div className="relative w-full">
<Label
htmlFor="username-input"
className="pointer-events-none absolute left-4 top-2 z-10 text-xs font-medium text-white/90"
>
Username
</Label>
<Input
id="username-input"
name="username"
placeholder="Enter your username"
type="text"
className="h-12 w-full rounded-xl bg-transparent px-4 pt-7 pb-2 text-sm text-white hover:border-[var(--border-onHover-Color)]! data-[focus-visible=true]:border-[var(--border-onHover-Color)]!"
/>
</div>
<div className="relative w-full">
<Label
htmlFor="password-input"
className="pointer-events-none absolute left-4 top-2 z-10 text-xs font-medium text-white/90"
>
Password
</Label>
<Input
id="password-input"
name="password"
placeholder="Enter your password"
type="password"
className="h-12 w-full rounded-xl bg-transparent px-4 pt-7 pb-2 text-sm text-white hover:border-[var(--border-onHover-Color)]! data-[focus-visible=true]:border-[var(--border-onHover-Color)]!"
/>
</div>
</div>
</Card.Content>
<Card.Footer className="flex flex-col items-center gap-3 w-full mt-2">
<Button type="submit" fullWidth>
Login
</Button>
<Link className="text-sm no-underline" href="#">
Forgot password?
</Link>
</Card.Footer>
</Form>
</Card>
</main>
</div>
)
}
export default Login
+63
View File
@@ -0,0 +1,63 @@
.hero {
position: relative;
.base,
.framework,
.vite {
inset-inline: 0;
margin: 0 auto;
}
.base {
width: 170px;
position: relative;
z-index: 0;
}
.framework,
.vite {
position: absolute;
}
.framework {
z-index: 1;
top: 34px;
height: 28px;
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
scale(1.4);
}
.vite {
z-index: 0;
top: 107px;
height: 26px;
width: auto;
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
scale(0.8);
}
}
h1 {
color: white;
font-family: Nunito, monospace;
}
.input {
border-color: #9ca3af;
background: var(--default-background);
font-family: Nunito, serif;
}
#center {
display: flex;
flex-direction: column;
gap: 25px;
place-content: center;
place-items: center;
flex-grow: 1;
@media (max-width: 1024px) {
padding: 32px 20px 24px;
gap: 18px;
}
}
+16
View File
@@ -0,0 +1,16 @@
import { StrictMode, useState } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import Journal from "./main/journal.tsx";
import Login from "./login/Login.tsx";
function App() {
const [loggedIn, setLoggedIn] = useState(false);
return loggedIn ? <Journal /> : <Login onLogin={() => setLoggedIn(true)} />;
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
+104
View File
@@ -0,0 +1,104 @@
import { Card, Button } from "@heroui/react";
import {Calendar} from "@gravity-ui/icons";
interface DayViewProps {
selectedDate: Date;
}
interface JournalEntry {
id: string;
title: string;
time: string;
excerpt: string;
mood: string;
moodColor: string;
tags: string[];
privacy: 'only-me' | 'shared';
sharedWith?: string;
}
const DAY_NAMES = [
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
] as const;
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
] as const;
const MOCK_ENTRIES: JournalEntry[] = [
/*{
id: '1',
title: 'Morning pages',
time: '8:15 AM',
excerpt: 'Woke up early and actually felt excited about the day for once. Small thing, but I noticed it.',
mood: '🙂',
moodColor: '#27AE60',
tags: ['morning routine'],
privacy: 'only-me',
},*/
];
function formatDate(date: Date): string {
return `${DAY_NAMES[date.getDay()]}, ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}`;
}
export function DayView({ selectedDate }: DayViewProps) {
const formatted = formatDate(selectedDate);
const entries = MOCK_ENTRIES;
return (
<div className="flex flex-1 flex-col gap-4">
<div className="flex items-start justify-between">
<div>
<h2 className="text-2xl font-bold text-[var(--text-h)]">{formatted}</h2>
<span className="text-sm text-[var(--text)]">
{entries.length === 0 ? 'No entries' : `${entries.length} entries`}
</span>
</div>
<button
aria-label="New entry"
className="flex size-9 items-center justify-center rounded-lg bg-[var(--card-background)] text-[var(--text-h)] transition-colors hover:bg-white/5"
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<path d="M12 5v14M5 12h14" />
</svg>
</button>
</div>
{/* Default empty card block*/}
<Card className="bg-[#131313] border-2 border-dashed border-[#2D2D32] rounded-3xl">
<Card.Content className="flex flex-col items-center justify-center gap-4 py-16 px-8 text-center">
<Calendar className="size-15" />
<Card.Title className="text-white text-lg font-bold">
No entries on this day yet
</Card.Title>
<Card.Description className="text-gray-400 text-lg">
Tap below to write about {formatted}.
</Card.Description>
<Button
variant="secondary"
className="font-bold h-10 px-8 bg-purple-500 shadow-lg shadow-purple-500/30 text-white/100 text-xl"
>
Write now
</Button>
</Card.Content>
</Card>
</div>
);
}
export default DayView;
+110
View File
@@ -0,0 +1,110 @@
import { Card, Avatar } from "@heroui/react";
export interface FriendNote {
id: string;
friendName: string;
friendInitial: string;
avatarColor: string;
timeAgo: string;
message: string;
entryTitle?: string;
}
interface FriendsNotesProps {
notes?: FriendNote[];
}
const MOCK_NOTES: FriendNote[] = [
/*{
id: '1',
friendName: 'Mia',
friendInitial: 'M',
avatarColor: '#C0392B',
timeAgo: '2d',
message: "This was the best day, let's do it again soon",
entryTitle: 'Beach day with Mia',
},
{
id: '2',
friendName: 'Sam',
friendInitial: 'S',
avatarColor: '#2980B9',
timeAgo: '5d',
message: "You've got this. Rooting for you!",
entryTitle: 'Before the interview',
},
{
id: '3',
friendName: 'Alex',
friendInitial: 'A',
avatarColor: '#27AE60',
timeAgo: '1w',
message: "Proud of the progress you're making lately.",
},*/
];
export function FriendsNotes({ notes = MOCK_NOTES }: FriendsNotesProps) {
return (
<div className="flex w-[280px] shrink-0 flex-col gap-4">
<div>
<h2 className="font-bold text-[var(--text-h)]">Friends notes</h2>
<p className="text-xs text-[var(--text)]">Encouragement from people you've shared with</p>
</div>
{notes.length === 0 ? (
<Card className="bg-[#131313] border-2 border-dashed border-[#2D2D32] rounded-3xl">
<Card.Content className="flex flex-col items-center justify-center gap-4 py-12 px-6 text-center">
<svg
className="size-10 text-[var(--text)]"
fill="none"
stroke="currentColor"
strokeWidth="1.5"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M8.625 9.75a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H8.25m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0H12m4.125 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm0 0h-.375m-13.5 3.01c0 1.6 1.123 2.994 2.707 3.227 1.087.16 2.185.283 3.293.369V21l4.184-4.183a1.14 1.14 0 01.778-.332 48.294 48.294 0 005.83-.498c1.585-.233 2.708-1.626 2.708-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0012 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018z"
/>
</svg>
<Card.Title className="text-white text-base font-bold">No notes yet</Card.Title>
<Card.Description className="text-gray-400 text-sm">
Notes from friends will show up here when you share an entry with them
</Card.Description>
</Card.Content>
</Card>
) : (
<div className="flex flex-col gap-3">
{notes.map(note => (
<Card key={note.id} className="bg-[var(--card-background)]">
<Card.Header className="flex-row items-center justify-between">
<div className="flex items-center gap-2">
<Avatar className="size-8">
<Avatar.Fallback
className="text-xs font-bold text-white border-none"
style={{ backgroundColor: note.avatarColor }}
>
{note.friendInitial}
</Avatar.Fallback>
</Avatar>
<Card.Title className="text-sm font-semibold text-[var(--text-h)]">
{note.friendName}
</Card.Title>
</div>
<span className="text-xs text-[var(--text)]">{note.timeAgo}</span>
</Card.Header>
<Card.Content>
<p className="text-sm text-[var(--text)]">{note.message}</p>
{note.entryTitle && (
<p className="mt-2 text-xs text-[var(--accent)]">on "{note.entryTitle}"</p>
)}
</Card.Content>
</Card>
))}
</div>
)}
</div>
);
}
export default FriendsNotes;
+173
View File
@@ -0,0 +1,173 @@
import { useState, useEffect } from "react";
import { Button, Calendar, Popover, PopoverDialog, PopoverArrow } from "@heroui/react";
import { parseDate } from "@internationalized/date";
import type { CalendarDate } from "@internationalized/date";
interface WeekStripProps {
selectedDate: Date;
onDateChange: (date: Date) => void;
entryDates?: Set<string>;
}
const DAY_NAMES = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'] as const;
const MONTH_NAMES = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December',
] as const;
function toISO(date: Date): string {
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
}
function getWeekSunday(date: Date): Date {
const d = new Date(date);
d.setDate(d.getDate() - d.getDay());
d.setHours(0, 0, 0, 0);
return d;
}
function addDays(date: Date, n: number): Date {
const d = new Date(date);
d.setDate(d.getDate() + n);
return d;
}
function toCalendarDate(date: Date): CalendarDate {
return parseDate(toISO(date));
}
function fromCalendarDate(cd: CalendarDate): Date {
return new Date(cd.year, cd.month - 1, cd.day);
}
export function WeekStrip({ selectedDate, onDateChange, entryDates = new Set() }: WeekStripProps) {
const [weekStart, setWeekStart] = useState(() => getWeekSunday(selectedDate));
const [calendarOpen, setCalendarOpen] = useState(false);
useEffect(() => {
setWeekStart(getWeekSunday(selectedDate));
}, [selectedDate]);
const weekDays = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));
const todayISO = toISO(new Date());
const selectedISO = toISO(selectedDate);
const monthYearLabel = `${MONTH_NAMES[weekStart.getMonth()]} ${weekStart.getFullYear()}`;
function handlePrev() {
setWeekStart(prev => addDays(prev, -7));
}
function handleNext() {
setWeekStart(prev => addDays(prev, 7));
}
function handleToday() {
const now = new Date();
setWeekStart(getWeekSunday(now));
onDateChange(now);
}
function handleCalendarChange(calDate: CalendarDate | null) {
if (!calDate) return;
const d = fromCalendarDate(calDate);
setWeekStart(getWeekSunday(d));
onDateChange(d);
setCalendarOpen(false);
}
return (
<div className="rounded-xl p-4">
<div className="mb-3 flex items-center justify-center gap-4">
<div className="flex items-center gap-15">
<button
onClick={handlePrev}
aria-label="Previous week"
className="flex size-7 items-center justify-center rounded-lg text-[var(--text)] transition-colors hover:bg-white/5"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M15 18l-6-6 6-6" />
</svg>
</button>
<Popover isOpen={calendarOpen} onOpenChange={setCalendarOpen}>
<Button variant="ghost" className="w-36 justify-center px-2 font-bold text-[var(--text-h)]">
{monthYearLabel}
</Button>
<Popover.Content offset={8}>
<PopoverDialog aria-label="Pick a date">
<PopoverArrow />
<Calendar
aria-label="Pick a date"
value={toCalendarDate(selectedDate)}
onChange={handleCalendarChange}
>
<Calendar.Header>
<Calendar.Heading />
<Calendar.NavButton slot="previous" />
<Calendar.NavButton slot="next" />
</Calendar.Header>
<Calendar.Grid>
<Calendar.GridHeader>
{(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
</Calendar.GridHeader>
<Calendar.GridBody>
{(date) => <Calendar.Cell date={date} />}
</Calendar.GridBody>
</Calendar.Grid>
</Calendar>
</PopoverDialog>
</Popover.Content>
</Popover>
<button
onClick={handleNext}
aria-label="Next week"
className="flex size-7 items-center justify-center rounded-lg text-[var(--text)] transition-colors hover:bg-white/5"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M9 18l6-6-6-6" />
</svg>
</button>
</div>
<Button className="text-[var(--button-text-color-secondary)]" variant="outline" size="sm" onPress={handleToday}>
Today
</Button>
</div>
<div className="grid grid-cols-7 gap-1">
{weekDays.map((date) => {
const iso = toISO(date);
const isSelected = iso === selectedISO;
const isToday = iso === todayISO;
const hasEntry = entryDates.has(iso);
return (
<button
key={iso}
onClick={() => onDateChange(date)}
className={[
'flex flex-col items-center gap-1 rounded-lg py-2 px-1 transition-colors',
isSelected
? 'bg-[var(--accent)] text-black'
: 'text-[var(--text-h)] hover:bg-white/5',
].join(' ')}
>
<span className={`text-[10px] font-semibold uppercase tracking-wider ${isSelected ? 'text-black/70' : 'text-[var(--text)]'}`}>
{DAY_NAMES[date.getDay()]}
</span>
<span className="text-lg font-bold leading-none">
{date.getDate()}
</span>
{(hasEntry || isToday) ? (
<span className={`size-1.5 rounded-full ${isSelected ? 'bg-black/40' : 'bg-[var(--accent)]'}`} />
) : (
<span className="size-1.5" />
)}
</button>
);
})}
</div>
</div>
);
}
export default WeekStrip;
+14
View File
@@ -0,0 +1,14 @@
@media (prefers-color-scheme: dark) {
:root {
--text: #9ca3af;
--text-h: #f3f4f6;
--bg: #16171d;
--border: #2e303a;
--code-bg: #1f2028;
--accent: #c084fc;
--accent-bg: rgba(192, 132, 252, 0.15);
--accent-border: rgba(192, 132, 252, 0.5);
--social-bg: rgba(47, 48, 58, 0.5);
--shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;
}
}
+115
View File
@@ -0,0 +1,115 @@
import {
Avatar,
Dropdown,
Label,
Separator,
SearchField,
RangeCalendar,
Button,
Popover,
PopoverDialog, PopoverArrow
} from "@heroui/react";
import {Calendar} from "@gravity-ui/icons";
import { useState } from "react";
import { WeekStrip } from "./WeekStrip";
import { FriendsNotes } from "./FriendsNotes";
import { DayView } from "./DayView";
import './journal.css'
export function Journal() {
const [selectedDate, setSelectedDate] = useState(new Date());
return (
<div className="p-8">
<Dropdown>
<Dropdown.Trigger aria-label="Open user menu" className="group flex items-center gap-3 cursor-pointer rounded-lg p-2 hover:bg-white/5">
<Avatar color="accent" className="rounded-lg">
<Avatar.Fallback className="rounded-lg">LR</Avatar.Fallback>
</Avatar>
<span className="flex flex-col">
<span className="text-sm font-bold text-[var(--text-h)]">Lena Rain</span>
<span className="text-xs text-[var(--text)]">Private journal</span>
</span>
<svg
className="ml-2 size-4 text-[var(--text)] transition-transform duration-200 group-aria-[expanded=true]:rotate-180"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path d="M6 9l6 6 6-6" />
</svg>
</Dropdown.Trigger>
<Dropdown.Popover className="bg-[var(--card-background)] rounded-xl">
<Dropdown.Menu
className="p-1"
onAction={(key) => console.log(key)}
>
<Dropdown.Item id="profile" textValue="Profile" className="rounded-lg text-[var(--text-h)]">
<Label className="font-medium text-white/90">Profile</Label>
</Dropdown.Item>
<Dropdown.Item id="settings" textValue="Settings" className="rounded-lg text-[var(--text-h)]">
<Label className="font-medium text-white/90">Settings</Label>
</Dropdown.Item>
<Separator className="bg-[var(--border-color)]" />
<Dropdown.Item id="logout" textValue="Log out" variant="danger" className="rounded-lg">
<Label>Log out</Label>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown.Popover>
</Dropdown>
<div className="container z-10 p-7 mx-auto flex flex-1 items-center justify-center gap-4">
<SearchField name="search" aria-label="Search for entries">
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input className="w-[280px]" placeholder="Search entries..." />
<SearchField.ClearButton />
</SearchField.Group>
</SearchField>
<div>
<Popover>
<Button variant="secondary">
<Calendar />
<span>Pick date range</span>
</Button>
<Popover.Content className="max-w-72" offset={10}>
<PopoverDialog aria-label="Date range picker">
<PopoverArrow />
<RangeCalendar aria-label="Trip dates" firstDayOfWeek="mon">
<RangeCalendar.Header>
<RangeCalendar.Heading />
<RangeCalendar.NavButton slot="previous" />
<RangeCalendar.NavButton slot="next" />
</RangeCalendar.Header>
<RangeCalendar.Grid>
<RangeCalendar.GridHeader>
{(day) => <RangeCalendar.HeaderCell>{day}</RangeCalendar.HeaderCell>}
</RangeCalendar.GridHeader>
<RangeCalendar.GridBody>
{(date) => <RangeCalendar.Cell date={date} />}
</RangeCalendar.GridBody>
</RangeCalendar.Grid>
</RangeCalendar>
</PopoverDialog>
</Popover.Content>
</Popover>
</div>
</div>
<div className="mx-auto max-w-3xl px-4">
<WeekStrip
selectedDate={selectedDate}
onDateChange={setSelectedDate}
/>
</div>
<div className="mx-auto mt-4 flex max-w-3xl gap-4 px-4">
<FriendsNotes />
<DayView selectedDate={selectedDate} />
</div>
</div>
);
}
export default Journal;
+26
View File
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023", "DOM"],
"module": "esnext",
"types": ["vite/client"],
"allowArbitraryExtensions": true,
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
+7
View File
@@ -0,0 +1,7 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
}
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "es2023",
"lib": ["ES2023"],
"types": ["node"],
"skipLibCheck": true,
/* Bundler mode */
"module": "nodenext",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true
},
"include": ["vite.config.ts"]
}
+8
View File
@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
})