Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ client/*.ts
client/*.js
client/wrangler.toml
server/dist
bun.lock
compose.yml
shell.nix
CLAUDE.local.md
biome.jsonc
1 change: 0 additions & 1 deletion .envrc

This file was deleted.

70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# イツヒマ

![ロゴ](./logo.png)

## 概要

とりあえずみんなの空いている時間を訊いてから、何を何時間やるか決めたい。そんな仲間うちでの日程調整に最適なツールです。

## 開発

### 要件

- Node.js
- npm
- Docker (開発用 DB のみに使用)

### セットアップ

依存関係のインストール

```sh
npm ci
```

Prisma Client の生成

```sh
cd server
npx prisma generate
```

`server/.env.sample` をコピーして `server/.env` を作成

`client/.env.local.sample` をコピーして `client/.env.local` を作成
### 起動

開発用データベースの起動

```sh
docker compose up
```

サーバー側とクライアント側をそれぞれ起動

```sh
npm run dev:server
```

```sh
npm run dev:client
```

cloudflare functions が必要な場合

```sh
npm run dev:functions
```

http://localhost:5173 にアクセスします。



### コードスタイル

コードのリント・フォーマット

```sh
npm run check
npm run fix
```
25 changes: 16 additions & 9 deletions biome.jsonc
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false,
"ignore": ["dist", "node_modules"]
"includes": ["**", "!**/dist", "!**/node_modules"]
},
"formatter": {
"enabled": true,
"lineWidth": 120,
"indentStyle": "space",
"indentWidth": 2
},
"organizeImports": {
"enabled": true
},
"assist": { "actions": { "source": { "organizeImports": "on" } } },
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"correctness": {
"noUnknownProperty": "off"
},
"complexity": {
"noForEach": "off"
"style": {
"noParameterAssign": "error",
"useAsConstAssertion": "error",
"useDefaultParameterLast": "error",
"useEnumInitializers": "error",
"useSelfClosingElements": "error",
"useSingleVarDeclarator": "error",
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"noInferrableTypes": "error",
"noUselessElse": "error"
},
"a11y": {
"useButtonType": "off"
"nursery": {
"useSortedClasses": "warn"
}
}
},
Expand Down
893 changes: 0 additions & 893 deletions bun.lock

This file was deleted.

6 changes: 0 additions & 6 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
"build:local": "tsc -b && vite build --mode dev",
"build:preview": "tsc -b && vite build --mode preview",
"build": "tsc -b && vite build",
"lint": "eslint .",
"format": "prettier . --check",
"lint:fix": "eslint . --fix",
"format:fix": "prettier . --write",
"preview": "vite preview"
},
"dependencies": {
Expand All @@ -37,15 +33,13 @@
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20250610.0",
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"daisyui": "^5.0.3",
"globals": "^15.15.0",
"hono": "^4.9.6",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.3.5"
}
}
2 changes: 1 addition & 1 deletion client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BrowserRouter, Outlet, Route, Routes } from "react-router";
import SubmissionPage from "./pages/eventId/Submission.tsx";
import HomePage from "./pages/Home.tsx";
import LandingPage from "./pages/Landing.tsx";
import NotFoundPage from "./pages/NotFound.tsx";
import ProjectPage from "./pages/Project.tsx";
import SubmissionPage from "./pages/eventId/Submission.tsx";

export default function App() {
return (
Expand Down
8 changes: 4 additions & 4 deletions client/src/components/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,9 @@ export const Calendar = ({ project, myGuestId, mySlotsRef, editMode }: Props) =>
const handleEventContent = useCallback((info: EventContentArg) => {
if (info.event.id === OTHERS_EVENT) {
return (
<div className="flex w-full h-full justify-center items-center">
<div className="flex h-full w-full items-center justify-center">
<div
className="badge badge-sm bg-gray-200 border-0 text-primary font-bold"
className="badge badge-sm border-0 bg-gray-200 font-bold text-primary"
data-tooltip-id="member-info"
data-tooltip-content={info.event.extendedProps.members?.join(", ")}
data-tooltip-place="top"
Expand All @@ -220,13 +220,13 @@ export const Calendar = ({ project, myGuestId, mySlotsRef, editMode }: Props) =>
}
if (info.event.id === MY_EVENT) {
return (
<div className="h-full w-full text-gray-600 overflow-hidden">{`${dayjs(info.event.start).format("HH:mm")} - ${dayjs(info.event.end).format("HH:mm")}`}</div>
<div className="h-full w-full overflow-hidden text-gray-600">{`${dayjs(info.event.start).format("HH:mm")} - ${dayjs(info.event.end).format("HH:mm")}`}</div>
);
}
}, []);

return (
<div className="flex-1 my-2" id="ih-cal-wrapper">
<div className="my-2 flex-1" id="ih-cal-wrapper">
<FullCalendar
ref={calendarRef}
plugins={[timeGridPlugin, interactionPlugin]}
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { NavLink } from "react-router";

export default function Header() {
return (
<div className="navbar bg-primary shadow-sm sticky top-0 left-0 z-50">
<div className="text-2xl text-white px-2 font-mplus">
<div className="navbar sticky top-0 left-0 z-50 bg-primary shadow-sm">
<div className="px-2 font-mplus text-2xl text-white">
<NavLink className="flex items-center gap-1" to="/">
<img src="/logo-white.svg" alt="logo" width={24} />
<span className="px-2">イツヒマ</span>
Expand Down
46 changes: 21 additions & 25 deletions client/src/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ export default function HomePage() {
<>
<Header />
{loading ? (
<div className="min-h-[calc(100dvh_-_64px)] bg-blue-50 flex items-center justify-center">
<div className="flex min-h-[calc(100dvh_-_64px)] items-center justify-center bg-blue-50">
<div className="py-4">
<span className="loading loading-dots loading-md text-gray-400" />
</div>
</div>
) : involvedProjects ? (
<ProjectDashboard involvedProjects={involvedProjects} />
) : (
<div className="min-h-[calc(100dvh_-_64px)] bg-blue-50 flex items-center justify-center">
<div className="flex min-h-[calc(100dvh_-_64px)] items-center justify-center bg-blue-50">
<EmptyState />
</div>
)}
Expand All @@ -68,15 +68,15 @@ function ProjectDashboard({ involvedProjects }: { involvedProjects: BriefProject
<div className="min-h-[calc(100dvh_-_64px)] bg-blue-50">
<div className="container mx-auto px-4 py-8">
{/* Hero Section */}
<div className="text-center mb-12">
<div className="flex items-center justify-center mt-2 mb-6">
<div className="mb-12 text-center">
<div className="mt-2 mb-6 flex items-center justify-center">
<img src="/logo.svg" alt="logo" width={48} className="mr-4" />
<h1 className="text-4xl text-primary font-mplus">イツヒマ</h1>
<h1 className="font-mplus text-4xl text-primary">イツヒマ</h1>
</div>
<p className="text-xl text-gray-600 mb-8">「いつヒマ?」で日程調整しよう</p>
<p className="mb-8 text-gray-600 text-xl">「いつヒマ?」で日程調整しよう</p>
<NavLink
to="/new"
className="btn btn-primary btn-lg px-8 py-4 text-lg shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1"
className="btn btn-primary btn-lg hover:-translate-y-1 transform px-8 py-4 text-lg shadow-lg transition-all duration-300 hover:shadow-xl"
>
<HiOutlinePlus className="mr-2" size={20} />
新しいイベントを作成
Expand All @@ -87,11 +87,11 @@ function ProjectDashboard({ involvedProjects }: { involvedProjects: BriefProject
<div className="space-y-8">
{/* All Projects */}
<section>
<h2 className="text-2xl font-bold text-gray-800 mb-6 flex items-center">
<h2 className="mb-6 flex items-center font-bold text-2xl text-gray-800">
<HiOutlineCalendar className="mr-3 text-gray-700" size={28} />
あなたのイベント
</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{sortedProjects.map((project) => (
<ProjectCard key={project.id} project={project} />
))}
Expand All @@ -108,16 +108,13 @@ function ProjectCard({ project }: { project: BriefProject }) {
return (
<NavLink
to={`/${project.id}`}
className={`group relative block bg-white rounded-xl shadow-lg hover:shadow-xl
transition-all duration-300 transform hover:-translate-y-1 overflow-hidden
border-l-4 ${project.isHost ? "border-primary" : "border-secondary"}
focus:outline-none focus:ring-4 focus:ring-primary/20`}
className={`group hover:-translate-y-1 relative block transform overflow-hidden rounded-xl border-l-4 bg-white shadow-lg transition-all duration-300 hover:shadow-xl ${project.isHost ? "border-primary" : "border-secondary"}focus:outline-none focus:ring-4 focus:ring-primary/20`}
aria-label={`「${project.name}」の詳細を見る`}
>
<div className="p-6">
<div className="flex justify-between items-start mb-4">
<div className="flex-1 mr-2">
<h3 className="text-xl font-semibold text-gray-800 mb-2 break-words">{project.name}</h3>
<div className="mb-4 flex items-start justify-between">
<div className="mr-2 flex-1">
<h3 className="mb-2 break-words font-semibold text-gray-800 text-xl">{project.name}</h3>
<span className={`badge badge-sm ${project.isHost ? "badge-primary" : "badge-secondary"}`}>
{project.isHost ? (
<>
Expand All @@ -137,15 +134,15 @@ function ProjectCard({ project }: { project: BriefProject }) {
<NavLink
to={`/${project.id}/edit`}
onClick={(e) => e.stopPropagation()}
className="btn btn-ghost btn-sm px-3 py-1 text-gray-500 hover:text-gray-700 hover:bg-gray-100 transition-all"
className="btn btn-ghost btn-sm px-3 py-1 text-gray-500 transition-all hover:bg-gray-100 hover:text-gray-700"
>
<HiOutlineCog size={14} />
<span className="text-xs">管理</span>
</NavLink>
)}
</div>

<div className="flex items-center text-gray-600 mb-4">
<div className="mb-4 flex items-center text-gray-600">
<HiOutlineCalendar className="mr-2" size={16} />
<span className="text-sm">
{formatDate(project.startDate.toLocaleDateString())} ~{formatDate(project.endDate.toLocaleDateString())}
Expand All @@ -154,8 +151,7 @@ function ProjectCard({ project }: { project: BriefProject }) {
</div>

<span
className={`absolute bottom-4 right-4 ${project.isHost ? "text-primary" : "text-secondary"} text-2xl pointer-events-none
group-hover:translate-x-1 transition-transform`}
className={`absolute right-4 bottom-4 ${project.isHost ? "text-primary" : "text-secondary"} pointer-events-none text-2xl transition-transform group-hover:translate-x-1`}
aria-hidden="true"
>
&rsaquo;
Expand All @@ -166,17 +162,17 @@ function ProjectCard({ project }: { project: BriefProject }) {

function EmptyState() {
return (
<div className="text-center py-16">
<div className="py-16 text-center">
<div className="mb-8">
<div className="w-32 h-32 mx-auto mb-6 bg-gray-100 rounded-full flex items-center justify-center">
<div className="mx-auto mb-6 flex h-32 w-32 items-center justify-center rounded-full bg-gray-100">
<HiOutlineCalendar className="text-gray-400" size={64} />
</div>
<h3 className="text-2xl font-semibold text-gray-800 mb-3">まだイベントがありません</h3>
<p className="text-gray-600 mb-8 max-w-md mx-auto">イベントを作成して、日程調整を始めましょう</p>
<h3 className="mb-3 font-semibold text-2xl text-gray-800">まだイベントがありません</h3>
<p className="mx-auto mb-8 max-w-md text-gray-600">イベントを作成して、日程調整を始めましょう</p>
</div>
<NavLink
to="/new"
className="btn btn-primary btn-lg px-8 py-4 text-lg shadow-lg hover:shadow-xl transition-all duration-300"
className="btn btn-primary btn-lg px-8 py-4 text-lg shadow-lg transition-all duration-300 hover:shadow-xl"
>
<HiOutlinePlus className="mr-2" size={20} />
イベントを作成する
Expand Down
Loading