diff --git a/apps/web/app/routes/board-route/current-user-standup-card.tsx b/apps/web/app/routes/board-route/current-user-standup-card.tsx index dcf5983..6d9f8c4 100644 --- a/apps/web/app/routes/board-route/current-user-standup-card.tsx +++ b/apps/web/app/routes/board-route/current-user-standup-card.tsx @@ -205,18 +205,21 @@ function CardContentUI({ }; } - useEffect(() => { - if (createStandupFetcher.state !== "idle" && createStandupFetcher.data) { - const { error } = createStandupFetcher.data; - if (error) { - toast.error(error); - console.error(error); - setIsEditing(true); - } else { - toast.success("Your standup has been saved"); + useEffect( + function handleCreateStandupResponse() { + if (createStandupFetcher.state !== "idle" && createStandupFetcher.data) { + const { error } = createStandupFetcher.data; + if (error) { + toast.error(error); + console.error(error); + setIsEditing(true); + } else { + toast.success("Your standup has been saved"); + } } - } - }, [createStandupFetcher.state, createStandupFetcher.data]); + }, + [createStandupFetcher.state, createStandupFetcher.data] + ); if (updateStandupFetcher.data) { const standup = updateStandupFetcher.data?.standup; @@ -239,21 +242,33 @@ function CardContentUI({ } } - useEffect(() => { - if (updateStandupFetcher.state !== "idle" && updateStandupFetcher.data) { - const error = updateStandupFetcher.data.error; - if (error) { - toast.error(error); - console.error(error); - setIsEditing(true); - } else { - toast.success("Your standup has been saved"); + useEffect( + function handleUpdateStandupResponse() { + if (updateStandupFetcher.state !== "idle" && updateStandupFetcher.data) { + const error = updateStandupFetcher.data.error; + if (error) { + toast.error(error); + console.error(error); + setIsEditing(true); + } else { + toast.success("Your standup has been saved"); + } } - } - }, [updateStandupFetcher.state, updateStandupFetcher.data]); + }, + [updateStandupFetcher.state, updateStandupFetcher.data] + ); const [isEditing, setIsEditing] = useState(!Boolean(currentUserTodayStandup)); + useEffect( + function switchToEditingModeWhenNoStandup() { + if (!currentUserTodayStandup) { + setIsEditing(true); + } + }, + [currentUserTodayStandup] + ); + function handleDynamicFormCancel() { setIsEditing(false); } @@ -344,9 +359,14 @@ function CardContentUI({ {schema.fields.map((field) => { + if (!currentUserTodayStandup) { + return null; + } + const value = ( - currentUserTodayStandup?.formData as DynamicFormValues + currentUserTodayStandup.formData as DynamicFormValues )[field.name]; + if (!value) { return null; } diff --git a/apps/web/app/routes/board-route/feed-view.tsx b/apps/web/app/routes/board-route/feed-view.tsx index acec301..56c6d51 100644 --- a/apps/web/app/routes/board-route/feed-view.tsx +++ b/apps/web/app/routes/board-route/feed-view.tsx @@ -180,9 +180,7 @@ function FeedViewUI({ )} */} - {isToday && currentUser && ( - - )} + {isToday && currentUser && } {/* Show all other standups in single column */} {groupStandups.map((standup) => { diff --git a/apps/web/app/routes/board-route/grid-view.tsx b/apps/web/app/routes/board-route/grid-view.tsx index 9b3defb..bf116d9 100644 --- a/apps/web/app/routes/board-route/grid-view.tsx +++ b/apps/web/app/routes/board-route/grid-view.tsx @@ -267,9 +267,7 @@ function GridViewUI({ )} */} - {isToday && currentUser && ( - - )} + {isToday && currentUser && } {groupStandups.map((standup) => { const user = usersMap.get(standup.userId); diff --git a/docs/releases/v0.1.0.md b/docs/releases/v0.1.0.md new file mode 100644 index 0000000..f3bb42e --- /dev/null +++ b/docs/releases/v0.1.0.md @@ -0,0 +1,16 @@ +# v0.1.0 + +This is the first alpha release of Standup Kiwi—a basic prototype serving as a proof of concept. It is not yet ready for practical use and is definitely full of bugs. Expect improvements in future updates as we refine functionality and stability! + +## Features + +- Board Creation – Create boards to organize your standups. +- Board Selection – Easily switch between boards using a dropdown menu. +- Dark Theme Support – Enjoy a sleek dark mode for a comfortable viewing experience. +- Board Management + - View a list of available boards. + - Open and view board details. +- Standup Management + - Add a standup entry for today. + - View a list of past standups for a board. +- Secure Signup with Email OTP – Sign up effortlessly using a one-time password (OTP) sent to your email. diff --git a/docs/releases/v0.2.0.md b/docs/releases/v0.2.0.md new file mode 100644 index 0000000..51b988a --- /dev/null +++ b/docs/releases/v0.2.0.md @@ -0,0 +1,19 @@ +# v0.2.0 + +This update focuses on backend robustness, groundwork for future features, and improvements to overall code quality. + +## Improvements + +- **Stricter TypeScript Configuration** + TypeScript settings have been tightened for better type safety. All existing type errors have been resolved. + +- **Database Reconnection Logic** + Added automatic database reconnection logic to handle connection drops more gracefully. + +- **Database Schema Update** + Refactored table and column names for improved consistency and readability. + +## In Progress + +- **Board Settings Page (Prototype)** + Initial UI structure implemented as a foundation for future settings functionality. Not yet connected to backend or state logic. diff --git a/docs/releases/v0.3.0.md b/docs/releases/v0.3.0.md new file mode 100644 index 0000000..311cb60 --- /dev/null +++ b/docs/releases/v0.3.0.md @@ -0,0 +1,29 @@ +# v0.3.0 + +This release introduces user-facing improvements, visual refinements, and infrastructure enhancements to support smoother development workflows. + +## Features + +- **Markdown Support in Standups** + Standup forms and cards now support basic markdown formatting for clearer, more expressive updates. + +- **Enhanced Authentication Flow** + Improved logic and structure for handling authentication across the app. + +## Bugfixes + +- **Dark Mode Logo Visibility** + Fixed an issue where the Kiwi logo was hard to see in dark mode by ensuring it appears in white. + +## Improvements + +- **Appearance State Highlighting** + UI elements now reflect their active state more clearly for improved usability. + +- **Root Redirect Handling** + Fixed routing behavior from the root path for a more seamless experience. + +## Infrastructure + +- **CI Setup for Monorepo** + GitHub Actions now validate builds and run type checks automatically to improve reliability during development. diff --git a/docs/releases/v0.3.1.md b/docs/releases/v0.3.1.md new file mode 100644 index 0000000..b6f33de --- /dev/null +++ b/docs/releases/v0.3.1.md @@ -0,0 +1,13 @@ +# v0.3.1 + +This release includes deployment configuration updates and a small bug fix to improve the user experience. + +## Improvements + +- Renamed and updated package.json scripts for consistency across web and API apps + +## Bugfixes + +- Fixed an issue where error messages were not properly displayed on the access and sign-in forms + +**Full Changelog**: https://github.com/kiwinight/standup-kiwi/compare/v0.3.0...v0.3.1 diff --git a/docs/releases/v0.4.0.md b/docs/releases/v0.4.0.md new file mode 100644 index 0000000..4c7d8a2 --- /dev/null +++ b/docs/releases/v0.4.0.md @@ -0,0 +1,28 @@ +# v0.4.0 + +This release introduces metadata enhancements, user-facing settings features, and deployment improvements to support a more polished user experience. + +## Features + +- **Board Name Editing in Settings** + Users can now edit the board name directly from the Settings page, making customization easier. + +## Improvements + +- **Website Metadata Configuration** + Added title, description, and Open Graph tags to improve SEO and social sharing. + +- **Mobile Viewport Handling** + Prevented zoom-in behavior on mobile when focusing on input fields for a smoother experience. + +## Bugfixes + +- **Deployment Fixes** + Resolved issues related to app deployment to ensure stable release delivery. + +## Infrastructure + +- **Release Branch Deployment** + The release branch is now deployed to the release subdomain, separating staging from production environments. + +**Full Changelog**: https://github.com/kiwinight/standup-kiwi/compare/v0.3.1...v0.4.0 diff --git a/docs/releases/v0.5.0.md b/docs/releases/v0.5.0.md new file mode 100644 index 0000000..ad11508 --- /dev/null +++ b/docs/releases/v0.5.0.md @@ -0,0 +1,43 @@ +# v0.5.0 + +This release introduces improved board settings, refined UI behavior, and foundational updates for better user experience and system reliability. + +## Features + +- **Keyboard Shortcuts for Standup Forms** + Users can now navigate and submit standup forms more efficiently with new keyboard shortcut support. + +- **Website and Blog Setup** + A public-facing website/blog has been set up as the new communication hub for updates and documentation. + +## Improvements + +- **Timezone Configuration** + Settings now include timezone selection, and the backend has been updated to reflect and persist these values accurately. + +- **Optimistic UI Refactor** + Board interactions are now more responsive and stable thanks to an internal refactor of the optimistic UI behavior. + +- **UI Polish** + Logo spacing and alignment have been improved. The board list in the toolbar now updates immediately after new board creation. Additionally, a hover state was added for board items in the navbar. + +- **Empty History Messaging** + Users will now see a friendly message when no history is available, instead of a blank section. + +## Bugfixes + +- **Dark Mode Refresh Issue** + Fixed an issue where dark mode would reset after a page refresh. + +- **Unauthorized Board Access** + Users attempting to access a board they don’t have permission to view will now see a 404 error. + +- **Error Boundary Navigation** + Introduced a "Back to Main" button on error boundaries to guide users safely back to the app. + +## Infrastructure + +- **Runtime Update** + Upgraded Node.js runtime for performance and security improvements. + +**Full Changelog**: https://github.com/kiwinight/standup-kiwi/compare/v0.4.0...v0.5.0 diff --git a/docs/releases/v0.6.0.md b/docs/releases/v0.6.0.md new file mode 100644 index 0000000..6e669f7 --- /dev/null +++ b/docs/releases/v0.6.0.md @@ -0,0 +1,30 @@ +# v0.6.0 + +This release includes metadata updates, analytics enhancements, and configuration improvements for smoother delivery and clearer communication. + +## Improvements + +- **Website Copy Update** + Refined website messaging for clearer communication and tone. + +- **Analytics Integration** + Analytics have been added both within the application and on the public-facing website to better understand user behavior. + +- **Metadata Enhancements** + Updated meta properties and values to improve SEO and social sharing. + +- **README.md Initialization** + Added a README file to improve onboarding and developer experience. + +- **Environment Variable for Button Behavior** + Added support for an environment variable to configure the 'Start Now' button logic. + +- **Library Version Upgrades** + Bumped several dependencies to ensure compatibility and stability. + +## Bugfixes + +- **Optimistic UI Reliability** + Fixed an issue where the optimistic UI behavior did not function correctly during some standup interactions. + +**Full Changelog**: https://github.com/kiwinight/standup-kiwi/compare/v0.5.0...v0.6.0 diff --git a/docs/releases/v0.7.0.md b/docs/releases/v0.7.0.md new file mode 100644 index 0000000..312c921 --- /dev/null +++ b/docs/releases/v0.7.0.md @@ -0,0 +1,38 @@ +# v0.7.0 + +This release includes new UI features, authentication improvements, and several Safari-related bug fixes. + +## Features + +- **Blog Setup** + Added a blog to the website for updates and announcements. + +- **Toast Notifications** + Added toast notifications to give users feedback when they perform actions. + +## Improvements + +- **Auto-Resizing Text Fields** + Text areas now grow taller automatically as you type more content. + +- **Auto-Refresh on Tab Focus** + The app refreshes itself when you switch back to the tab to keep data current. + +- **Search Engine Prevention** + Prevented the app subdomain from showing up in search results. + +- **Session Settings** + Updated authentication session configuration for better reliability. + +- **Database Updates** + Cleaned up some database structure for better consistency. + +## Bugfixes + +- **Safari Login Fix** + Fixed login issues that were happening on Mac Safari with localhost. + +- **Safari Display Fix** + Fixed UI display problems that made things look weird on Safari. + +**Full Changelog**: https://github.com/kiwinight/standup-kiwi/compare/v0.6.0...v0.7.0 diff --git a/docs/releases/v0.8.0.md b/docs/releases/v0.8.0.md new file mode 100644 index 0000000..d15273a --- /dev/null +++ b/docs/releases/v0.8.0.md @@ -0,0 +1,47 @@ +# v0.8.0 + +This release introduces shared boards with collaborators. You can now turn any board into a shared space: invite teammates, assign roles, and manage membership — all in-app. + +## Features + +- **Shared Boards and Collaborators** + Invite collaborators, assign roles (including a new member role), update/remove collaborators, and leave a board. + +- **Board Invitations** + Create and regenerate invitation links; accept invitations via dedicated routes. + +- **Richer Standup Views** + New grid and feed views with support for multiple standups per day and a focused “today’s standup” for the current user. + +- **New Landing Page** + A refreshed website with an interactive demo and animations. + +## Improvements + +- **Personal Board Layout Alignment** + The personal board experience is now aligned with the team board layout. + +- **Clearer Empty States** + Improved empty-history UI for past standups. + +- **Authentication Structure** + Introduced a permissive auth guard/service and refined session/token handling. + +- **Shared Types Across API & Web** + Consolidated TypeScript types to improve consistency and developer experience. + +- **Auth Path Rename** + Cleaned up routing and renamed email auth paths to `/auth/email/...`. + +- **Database Updates** + Schema and migrations to support collaborator roles and invitation workflow. + +## Bugfixes + +- **Safari Reliability** + Fixed Safari-specific runtime and login issues. + +- **Route Loader Stability** + Resolved a crash caused by a non-null assertion during route loader data destructuring. + +**Full Changelog**: https://github.com/kiwinight/standup-kiwi/compare/v0.7.0...v0.8.0 diff --git a/package.json b/package.json index 3a03d5f..66e8926 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "private": false, "scripts": { "dev:web": "pnpm --filter standup-kiwi-web dev", - "dev:api": "pnpm --filter standup-kiwi-api dev", - "dev:website": "pnpm --filter standup-kiwi-website dev" + "dev:api": "pnpm --filter standup-kiwi-api dev" } } diff --git a/sites/website/.gitignore b/sites/website/.gitignore deleted file mode 100644 index 016b59e..0000000 --- a/sites/website/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# build output -dist/ - -# generated types -.astro/ - -# dependencies -node_modules/ - -# logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - -# environment variables -.env -.env.production - -# macOS-specific files -.DS_Store - -# jetbrains setting folder -.idea/ diff --git a/sites/website/.vscode/extensions.json b/sites/website/.vscode/extensions.json deleted file mode 100644 index 22a1505..0000000 --- a/sites/website/.vscode/extensions.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "recommendations": ["astro-build.astro-vscode"], - "unwantedRecommendations": [] -} diff --git a/sites/website/.vscode/launch.json b/sites/website/.vscode/launch.json deleted file mode 100644 index d642209..0000000 --- a/sites/website/.vscode/launch.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "command": "./node_modules/.bin/astro dev", - "name": "Development server", - "request": "launch", - "type": "node-terminal" - } - ] -} diff --git a/sites/website/README.md b/sites/website/README.md deleted file mode 100644 index 55ccc6e..0000000 --- a/sites/website/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Standup Kiwi Website - -This is the official website for [Standup Kiwi](https://standupkiwi.com). diff --git a/sites/website/astro.config.mjs b/sites/website/astro.config.mjs deleted file mode 100644 index a435046..0000000 --- a/sites/website/astro.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -// @ts-check -import { defineConfig } from "astro/config"; -import mdx from "@astrojs/mdx"; -import sitemap from "@astrojs/sitemap"; -import tailwindcss from "@tailwindcss/vite"; -import react from "@astrojs/react"; - -// https://astro.build/config -export default defineConfig({ - site: "https://standupkiwi.com", - vite: { - plugins: [tailwindcss()], - }, - - integrations: [react(), mdx(), sitemap()], -}); diff --git a/sites/website/package.json b/sites/website/package.json deleted file mode 100644 index 0219a9a..0000000 --- a/sites/website/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "standup-kiwi-website", - "type": "module", - "version": "0.0.1", - "scripts": { - "dev": "astro dev", - "build": "astro build", - "preview": "astro preview", - "astro": "astro" - }, - "dependencies": { - "@astrojs/mdx": "^4.3.0", - "@astrojs/react": "^4.2.5", - "@astrojs/rss": "^4.0.11", - "@astrojs/sitemap": "^3.4.0", - "@radix-ui/react-icons": "^1.3.2", - "@radix-ui/themes": "^3.1.6", - "@tailwindcss/vite": "^4.1.11", - "@types/react": "^19.1.2", - "@types/react-dom": "^19.1.2", - "astro": "^5.8.0", - "lucide-react": "^0.525.0", - "motion": "^12.23.12", - "radix-ui": "^1.4.2", - "react": "^19.1.0", - "react-dom": "^19.1.0", - "sharp": "^0.34.3", - "tailwindcss": "^4.1.11" - }, - "devDependencies": { - "@tailwindcss/typography": "^0.5.16" - } -} diff --git a/sites/website/public/introducing-standup-kiwi-open-source-tool-daily-standups-hero-image.png b/sites/website/public/introducing-standup-kiwi-open-source-tool-daily-standups-hero-image.png deleted file mode 100644 index e5edce4..0000000 Binary files a/sites/website/public/introducing-standup-kiwi-open-source-tool-daily-standups-hero-image.png and /dev/null differ diff --git a/sites/website/src/assets/apple-touch-icon.png b/sites/website/src/assets/apple-touch-icon.png deleted file mode 100644 index 325b2e1..0000000 Binary files a/sites/website/src/assets/apple-touch-icon.png and /dev/null differ diff --git a/sites/website/src/assets/favicon-16x16.png b/sites/website/src/assets/favicon-16x16.png deleted file mode 100644 index d1aa809..0000000 Binary files a/sites/website/src/assets/favicon-16x16.png and /dev/null differ diff --git a/sites/website/src/assets/favicon-32x32.png b/sites/website/src/assets/favicon-32x32.png deleted file mode 100644 index 0d34869..0000000 Binary files a/sites/website/src/assets/favicon-32x32.png and /dev/null differ diff --git a/sites/website/src/assets/og-image.png b/sites/website/src/assets/og-image.png deleted file mode 100644 index 11ad779..0000000 Binary files a/sites/website/src/assets/og-image.png and /dev/null differ diff --git a/sites/website/src/assets/overview/autumnal-peach-gradient.png b/sites/website/src/assets/overview/autumnal-peach-gradient.png deleted file mode 100644 index cc908e0..0000000 Binary files a/sites/website/src/assets/overview/autumnal-peach-gradient.png and /dev/null differ diff --git a/sites/website/src/assets/overview/good-vibes-gradient.png b/sites/website/src/assets/overview/good-vibes-gradient.png deleted file mode 100644 index 1e5bf43..0000000 Binary files a/sites/website/src/assets/overview/good-vibes-gradient.png and /dev/null differ diff --git a/sites/website/src/assets/overview/how-it-works-1080p-30fps-web-16x9.mp4 b/sites/website/src/assets/overview/how-it-works-1080p-30fps-web-16x9.mp4 deleted file mode 100644 index 8f2add1..0000000 Binary files a/sites/website/src/assets/overview/how-it-works-1080p-30fps-web-16x9.mp4 and /dev/null differ diff --git a/sites/website/src/assets/overview/how-it-works-1080p-60fps-web-3x4.mp4 b/sites/website/src/assets/overview/how-it-works-1080p-60fps-web-3x4.mp4 deleted file mode 100644 index 4c8ac82..0000000 Binary files a/sites/website/src/assets/overview/how-it-works-1080p-60fps-web-3x4.mp4 and /dev/null differ diff --git a/sites/website/src/assets/overview/standup-kiwi-board-demo.png b/sites/website/src/assets/overview/standup-kiwi-board-demo.png deleted file mode 100644 index 4ee23ea..0000000 Binary files a/sites/website/src/assets/overview/standup-kiwi-board-demo.png and /dev/null differ diff --git a/sites/website/src/assets/overview/standup-kiwi-responsive-mockup.png b/sites/website/src/assets/overview/standup-kiwi-responsive-mockup.png deleted file mode 100644 index d4ab4fc..0000000 Binary files a/sites/website/src/assets/overview/standup-kiwi-responsive-mockup.png and /dev/null differ diff --git a/sites/website/src/components/base-head.astro b/sites/website/src/components/base-head.astro deleted file mode 100644 index 93ec6e5..0000000 --- a/sites/website/src/components/base-head.astro +++ /dev/null @@ -1,35 +0,0 @@ ---- -import favicon16 from "../assets/favicon-16x16.png"; -import favicon32 from "../assets/favicon-32x32.png"; -import appleTouchIcon from "../assets/apple-touch-icon.png"; -import PostHogScript from "./posthog-script.astro"; -import { SITE_NAME } from "../constants"; - -const canonicalURL = new URL(Astro.url.pathname, Astro.site); ---- - - - - - - - - - - - - - - - - - - diff --git a/sites/website/src/components/blur-fade.tsx b/sites/website/src/components/blur-fade.tsx deleted file mode 100644 index 4759812..0000000 --- a/sites/website/src/components/blur-fade.tsx +++ /dev/null @@ -1,83 +0,0 @@ -// Based on https://magicui.design/docs/components/blur-fade - -import { - AnimatePresence, - motion, - useInView, - type UseInViewOptions, - type Variants, - type MotionProps, -} from "motion/react"; -import { useRef } from "react"; - -// type MarginType = UseInViewOptions["margin"]; - -interface BlurFadeProps extends MotionProps { - children: React.ReactNode; - className?: string; - variant?: { - hidden: { y: number }; - visible: { y: number }; - }; - // duration?: number; - delay?: number; - offset?: number; - direction?: "up" | "down" | "left" | "right"; - inView?: boolean; - // inViewMargin?: MarginType; - blur?: string; -} - -export function BlurFade({ - children, - className, - variant, - // duration = 0.25, - delay = 0, - offset = 8, - direction = "down", - inView = false, - blur = "8px", - ...props -}: BlurFadeProps) { - const ref = useRef(null); - const inViewResult = useInView(ref, { - once: false, - margin: "200px 0px -200px 0px", - }); - const isInView = !inView || inViewResult; - const defaultVariants: Variants = { - hidden: { - [direction === "left" || direction === "right" ? "x" : "y"]: - direction === "right" || direction === "down" ? -offset : offset, - opacity: 0, - filter: `blur(${blur})`, - }, - visible: { - [direction === "left" || direction === "right" ? "x" : "y"]: 0, - opacity: 1, - filter: `blur(0px)`, - }, - }; - const combinedVariants = variant || defaultVariants; - return ( - - - {children} - - - ); -} diff --git a/sites/website/src/components/content-head.astro b/sites/website/src/components/content-head.astro deleted file mode 100644 index 52945fe..0000000 --- a/sites/website/src/components/content-head.astro +++ /dev/null @@ -1,24 +0,0 @@ ---- -import ogImage from "../assets/og-image.png"; -import { SITE_NAME } from "../constants"; - -const { title, description } = Astro.props; ---- - -{title} • {SITE_NAME} - - - - - - - - - - - - - - - - diff --git a/sites/website/src/components/line-shadow-text.tsx b/sites/website/src/components/line-shadow-text.tsx deleted file mode 100644 index 6f85a1d..0000000 --- a/sites/website/src/components/line-shadow-text.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { motion, type MotionProps } from "motion/react"; -import { cn } from "../lib/utils"; - -interface LineShadowTextProps - extends Omit, keyof MotionProps>, - MotionProps { - shadowColor?: string; - as?: React.ElementType; -} - -export function LineShadowText({ - children, - shadowColor = "black", - className, - as: Component = "span", - ...props -}: LineShadowTextProps) { - const MotionComponent = motion.create(Component); - const content = typeof children === "string" ? children : null; - - if (!content) { - throw new Error("LineShadowText only accepts string content"); - } - - return ( - - {content} - - ); -} diff --git a/sites/website/src/components/nav-bar.tsx b/sites/website/src/components/nav-bar.tsx deleted file mode 100644 index fa63dc2..0000000 --- a/sites/website/src/components/nav-bar.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import { - ExternalLinkIcon, - HamburgerMenuIcon, - ListBulletIcon, -} from "@radix-ui/react-icons"; -import { - Badge, - Box, - Button, - DropdownMenu, - Flex, - Grid, - IconButton, - Text, -} from "@radix-ui/themes"; - -export function NavBar() { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - - - Blog - - - - GitHub - - - - - - - - - - ); -} diff --git a/sites/website/src/components/posthog-script.astro b/sites/website/src/components/posthog-script.astro deleted file mode 100644 index 86dd81d..0000000 --- a/sites/website/src/components/posthog-script.astro +++ /dev/null @@ -1,57 +0,0 @@ - diff --git a/sites/website/src/components/scroll-fade.tsx b/sites/website/src/components/scroll-fade.tsx deleted file mode 100644 index 7dcc3a9..0000000 --- a/sites/website/src/components/scroll-fade.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { - motion, - useInView, - useScroll, - useTransform, - type UseInViewOptions, - type MotionProps, -} from "motion/react"; -import { useRef } from "react"; - -type MarginType = UseInViewOptions["margin"]; - -interface ScrollFadeProps extends MotionProps { - children: React.ReactNode; - className?: string; - duration?: number; - delay?: number; - offset?: number; - direction?: "up" | "down" | "left" | "right"; - inViewMargin?: MarginType; - threshold?: number; - parallaxIntensity?: number; // How much the section follows scroll (0 = no parallax, 1 = full parallax) - enableParallax?: boolean; - // Note: Fade effects removed for better performance - now only provides parallax movement -} - -export function ScrollFade({ - children, - className, - duration = 0.6, - delay = 0, - offset = 50, - direction = "up", - inViewMargin = "-100px", - threshold = 0.1, - parallaxIntensity = 0.3, - enableParallax = false, - ...props -}: ScrollFadeProps) { - const ref = useRef(null); - const isInView = useInView(ref, { - once: false, // Allow repeated animations - margin: inViewMargin, - amount: threshold, - }); - - // Track scroll progress for parallax effect - const { scrollYProgress } = useScroll({ - target: ref, - offset: ["start end", "end start"], // Better range for fade effects - }); - - // Transform scroll progress to y movement - const parallaxY = useTransform( - scrollYProgress, - [0, 0.5, 1], - [offset, 0, -offset] - ); - - // No opacity animation for better performance - // const scrollOpacity = useTransform( - // scrollYProgress, - // fadeRange, // Use configurable fade range - // [0, 1, 1, 1] // Fade in and stay visible (no fade out) - // ); - - // Debug logging - commented out for production - // useEffect(() => { - // const unsubscribe = scrollYProgress.on("change", (value) => { - // console.log( - // "ScrollFade - scrollYProgress:", - // value, - // "opacity:", - // scrollOpacity.get(), - // "y:", - // parallaxY.get() - // ); - // }); - // return unsubscribe; - // }, [scrollYProgress, scrollOpacity, parallaxY]); - - const getInitialValues = () => { - switch (direction) { - case "down": - return { y: -offset, x: 0 }; - case "left": - return { x: offset, y: 0 }; - case "right": - return { x: -offset, y: 0 }; - case "up": - default: - return { y: offset, x: 0 }; - } - }; - - const initialValues = getInitialValues(); - - if (enableParallax) { - // For parallax mode, use motion values directly (no fade for performance) - return ( - - {children} - - ); - } else { - // For non-parallax mode, use regular animations - return ( - - {children} - - ); - } -} diff --git a/sites/website/src/components/text-animate.tsx b/sites/website/src/components/text-animate.tsx deleted file mode 100644 index 04ad4f7..0000000 --- a/sites/website/src/components/text-animate.tsx +++ /dev/null @@ -1,413 +0,0 @@ -import { cn } from "../lib/utils"; -import { - AnimatePresence, - motion, - type MotionProps, - type Variants, -} from "motion/react"; -import { type ElementType, memo } from "react"; - -type AnimationType = "text" | "word" | "character" | "line"; -type AnimationVariant = - | "fadeIn" - | "blurIn" - | "blurInUp" - | "blurInDown" - | "slideUp" - | "slideDown" - | "slideLeft" - | "slideRight" - | "scaleUp" - | "scaleDown"; - -interface TextAnimateProps extends MotionProps { - /** - * The text content to animate - */ - children: string; - /** - * The class name to be applied to the component - */ - className?: string; - /** - * The class name to be applied to each segment - */ - segmentClassName?: string; - /** - * The delay before the animation starts - */ - delay?: number; - /** - * The duration of the animation - */ - duration?: number; - /** - * Custom motion variants for the animation - */ - variants?: Variants; - /** - * The element type to render - */ - as?: ElementType; - /** - * How to split the text ("text", "word", "character") - */ - by?: AnimationType; - /** - * Whether to start animation when component enters viewport - */ - startOnView?: boolean; - /** - * Whether to animate only once - */ - once?: boolean; - /** - * The animation preset to use - */ - animation?: AnimationVariant; -} - -const staggerTimings: Record = { - text: 0.06, - word: 0.05, - character: 0.03, - line: 0.06, -}; - -const defaultContainerVariants = { - hidden: { opacity: 1 }, - show: { - opacity: 1, - transition: { - delayChildren: 0, - staggerChildren: 0.05, - }, - }, - exit: { - opacity: 0, - transition: { - staggerChildren: 0.05, - staggerDirection: -1, - }, - }, -}; - -const defaultItemVariants: Variants = { - hidden: { opacity: 0 }, - show: { - opacity: 1, - }, - exit: { - opacity: 0, - }, -}; - -const defaultItemAnimationVariants: Record< - AnimationVariant, - { container: Variants; item: Variants } -> = { - fadeIn: { - container: defaultContainerVariants, - item: { - hidden: { opacity: 0, y: 20 }, - show: { - opacity: 1, - y: 0, - transition: { - duration: 0.3, - }, - }, - exit: { - opacity: 0, - y: 20, - transition: { duration: 0.3 }, - }, - }, - }, - blurIn: { - container: defaultContainerVariants, - item: { - hidden: { opacity: 0, filter: "blur(10px)" }, - show: { - opacity: 1, - filter: "blur(0px)", - transition: { - duration: 0.3, - }, - }, - exit: { - opacity: 0, - filter: "blur(10px)", - transition: { duration: 0.3 }, - }, - }, - }, - blurInUp: { - container: defaultContainerVariants, - item: { - hidden: { opacity: 0, filter: "blur(10px)", y: 20 }, - show: { - opacity: 1, - filter: "blur(0px)", - y: 0, - transition: { - y: { duration: 0.3 }, - opacity: { duration: 0.4 }, - filter: { duration: 0.3 }, - }, - }, - exit: { - opacity: 0, - filter: "blur(10px)", - y: 20, - transition: { - y: { duration: 0.3 }, - opacity: { duration: 0.4 }, - filter: { duration: 0.3 }, - }, - }, - }, - }, - blurInDown: { - container: defaultContainerVariants, - item: { - hidden: { opacity: 0, filter: "blur(10px)", y: -20 }, - show: { - opacity: 1, - filter: "blur(0px)", - y: 0, - transition: { - y: { duration: 0.3 }, - opacity: { duration: 0.4 }, - filter: { duration: 0.3 }, - }, - }, - }, - }, - slideUp: { - container: defaultContainerVariants, - item: { - hidden: { y: 20, opacity: 0 }, - show: { - y: 0, - opacity: 1, - transition: { - duration: 0.3, - }, - }, - exit: { - y: -20, - opacity: 0, - transition: { - duration: 0.3, - }, - }, - }, - }, - slideDown: { - container: defaultContainerVariants, - item: { - hidden: { y: -20, opacity: 0 }, - show: { - y: 0, - opacity: 1, - transition: { duration: 0.3 }, - }, - exit: { - y: 20, - opacity: 0, - transition: { duration: 0.3 }, - }, - }, - }, - slideLeft: { - container: defaultContainerVariants, - item: { - hidden: { x: 20, opacity: 0 }, - show: { - x: 0, - opacity: 1, - transition: { duration: 0.3 }, - }, - exit: { - x: -20, - opacity: 0, - transition: { duration: 0.3 }, - }, - }, - }, - slideRight: { - container: defaultContainerVariants, - item: { - hidden: { x: -20, opacity: 0 }, - show: { - x: 0, - opacity: 1, - transition: { duration: 0.3 }, - }, - exit: { - x: 20, - opacity: 0, - transition: { duration: 0.3 }, - }, - }, - }, - scaleUp: { - container: defaultContainerVariants, - item: { - hidden: { scale: 0.5, opacity: 0 }, - show: { - scale: 1, - opacity: 1, - transition: { - duration: 0.3, - scale: { - type: "spring", - damping: 15, - stiffness: 300, - }, - }, - }, - exit: { - scale: 0.5, - opacity: 0, - transition: { duration: 0.3 }, - }, - }, - }, - scaleDown: { - container: defaultContainerVariants, - item: { - hidden: { scale: 1.5, opacity: 0 }, - show: { - scale: 1, - opacity: 1, - transition: { - duration: 0.3, - scale: { - type: "spring", - damping: 15, - stiffness: 300, - }, - }, - }, - exit: { - scale: 1.5, - opacity: 0, - transition: { duration: 0.3 }, - }, - }, - }, -}; - -const TextAnimateBase = ({ - children, - delay = 0, - duration = 0.3, - variants, - className, - segmentClassName, - as: Component = "p", - startOnView = true, - once = false, - by = "word", - animation = "fadeIn", - ...props -}: TextAnimateProps) => { - const MotionComponent = motion.create(Component); - - let segments: string[] = []; - switch (by) { - case "word": - segments = children.split(/(\s+)/); - break; - case "character": - segments = children.split(""); - break; - case "line": - segments = children.split("\n"); - break; - case "text": - default: - segments = [children]; - break; - } - - const finalVariants = variants - ? { - container: { - hidden: { opacity: 0 }, - show: { - opacity: 1, - transition: { - opacity: { duration: 0.01, delay }, - delayChildren: delay, - staggerChildren: duration / segments.length, - }, - }, - exit: { - opacity: 0, - transition: { - staggerChildren: duration / segments.length, - staggerDirection: -1, - }, - }, - }, - item: variants, - } - : animation - ? { - container: { - ...defaultItemAnimationVariants[animation].container, - show: { - ...defaultItemAnimationVariants[animation].container.show, - transition: { - delayChildren: delay, - staggerChildren: duration / segments.length, - }, - }, - exit: { - ...defaultItemAnimationVariants[animation].container.exit, - transition: { - staggerChildren: duration / segments.length, - staggerDirection: -1, - }, - }, - }, - item: defaultItemAnimationVariants[animation].item, - } - : { container: defaultContainerVariants, item: defaultItemVariants }; - - return ( - - - {segments.map((segment, i) => ( - - {segment} - - ))} - - - ); -}; - -// Export the memoized version -export const TextAnimate = memo(TextAnimateBase); diff --git a/sites/website/src/constants.ts b/sites/website/src/constants.ts deleted file mode 100644 index b06e30a..0000000 --- a/sites/website/src/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Place any global data in this file. -// You can import this data from anywhere in your site by using the `import` keyword. - -export const SITE_NAME = "Standup Kiwi"; -export const SITE_DESCRIPTION = - "Standup Kiwi is an open-source standup board for teams and solo experts — a calm, clear, and effortless way to share and record daily standups, whether async or live."; diff --git a/sites/website/src/content.config.ts b/sites/website/src/content.config.ts deleted file mode 100644 index 7b6f671..0000000 --- a/sites/website/src/content.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { glob } from "astro/loaders"; -import { defineCollection, z } from "astro:content"; - -const blog = defineCollection({ - // Load Markdown and MDX files in the `src/content/blog/` directory. - loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }), - // Type-check frontmatter using a schema - schema: z.object({ - title: z.string(), - description: z.string(), - // Transform string to Date object - pubDate: z.coerce.date(), - updatedDate: z.coerce.date().optional(), - author: z.string(), - authorTitle: z.string().optional(), - authorLink: z.string().optional(), - heroImage: z.string().optional(), - }), -}); - -export const collections = { blog }; diff --git a/sites/website/src/content/blog/introducing-standup-kiwi-open-source-tool-daily-standups.md b/sites/website/src/content/blog/introducing-standup-kiwi-open-source-tool-daily-standups.md deleted file mode 100644 index 1dc73e5..0000000 --- a/sites/website/src/content/blog/introducing-standup-kiwi-open-source-tool-daily-standups.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: "Introducing Standup Kiwi - The Open Source Tool for Daily Standups" -slug: "introducing-standup-kiwi-open-source-tool-daily-standups" -description: "Standup Kiwi is an open-source tool for easy standups with a user-friendly design. Perfect for remote teams and individuals, it helps boost productivity and has plans for more features." -pubDate: "2025-06-17" -heroImage: "/introducing-standup-kiwi-open-source-tool-daily-standups-hero-image.png" -# Hero image generated from - https://og-playground.vercel.app/?share=vVJdT8IwFP0rTYlBkxFREcmCJH6HmPiCiS-8lPVuXOzaZbsT5rL_bsuoCD_APiy35zTnnHt3ax4ZCTzkY4lfc81YQZWC27p2NWMSi0yJKmTdWMGmG7ToEjBZkgUv-v0TD65R0vIIEwoTPSVIC0tEoAlyT63KgjCuHowFtdM6pJ3dI-YQERrtWKPKVHt2IaLPJDelltNUJGB5hRpE3ktyIdEKnZJhZDKWu6AB69xEVwKkLQbxYAjXZ15IAVnTWSYi1EnIepfe3sZ6Fikq1_r0bzCCDd25to4iN437Tua6fcaYH6g7h0N1x_ZjcivRibfHq3vvj92Eh_3-ETPDb9vvYORR59vWkxkJLcuMveIax-fW_jfN_2d5M2uGmt0DiX2Uud7XPOAmcz-34GHNt8vDw5G14O128XDgLhIWZcLDWKgCAg6pWeF7lbmFpfX2ZnVclKd0AZKHlJfQBJzEwr542S1DwZsf -author: "H. Alex Kwon" -authorTitle: "Product Engineer" -authorLink: "https://x.com/hyokualexkwon" ---- - -Hey there! I'm Alex, a developer who has spent the last few months building an open source standup tool called **Standup Kiwi**. - -Today, I'm excited to introduce Standup Kiwi with its first public beta — a lightweight, open source tool designed specifically for async daily standups. It's still in the very earliest stages of beta, but I think it's already useful enough to try out. And it's completely free during this beta period. - -## Why I built this - -My previous team used to run our standup meetings and async updates using Jira boards — mostly just to keep a record. But honestly, **Jira felt way too heavy** for something as lightweight as a daily standup. - -We switched to Slack for a while, but things quickly felt **too scattered and unstructured**. That's when I started wishing there was a simple, board-style tool designed **just for standups**. - -So I decided to build it. - -## How it works - -1. **Create a board** — Set up a board for your team, project, or personal tracking. Takes seconds. - -2. **Write your standup** — Share what you did yesterday, what you're planning today, and anything blocking progress. Simple markdown editor, no complicated forms. - -3. **Browse standups** — Catch up on today's or past standups. Each update becomes a clean, organized card on the board. - -4. **Repeat** — Use it async or alongside live meetings. Your standup history is always there to review and reflect. - -## How I use it - -I start each morning with Standup Kiwi, using my personal board. It's become my favorite way to kick off the day — just me, my coffee, and a quick reflection on my work. - -Every morning, I take 5 minutes to write down what I did yesterday and plan what I'm doing today. For example, yesterday I might write "Fixed the API authentication bug that was blocking user login" and today "Tackling mobile responsive design for the dashboard." This simple habit has been a game-changer. It helps me: - -- Clear my mind and start fresh -- Remember important tasks I might have forgotten -- Feel more organized and less stressed -- Track my progress over time - -Once team boards are ready (coming soon!), I'm planning to roll this out to my open source group — and even at work. I genuinely think we could **replace our daily standup meetings**, or at least have a clearer, async-friendly view of **what everyone's working on**. - -## Who it's for - -Standup Kiwi works well for anyone who wants to stay organized and productive. Especially: - -- **Solo workers** — developers, designers, writers who want to track daily progress -- **Remote teams** — especially those working across time zones -- **Groups and communities** — who want a lightweight way to stay in sync - -If you've ever started your day wondering "what was I doing again?" or felt lost in endless chat threads — Standup Kiwi might be just what you need. - -For me, it's become a simple routine: 5 minutes each morning leads to a clearer mind, better organization, and actually being able to see my progress over time. - -## What makes it different - -There are many tools out there for team updates, but Standup Kiwi takes a fresh approach: - -- **Simple yet powerful** - Unlike Jira or other heavy tools, Standup Kiwi focuses purely on standups. Write in markdown, organize in boards, and find updates instantly. - -- **Clean and focused** - Simple design with dark mode support. A dedicated space that helps you stay on track. - -- **Open source and flexible** - See exactly how your data is handled. Host it yourself or use our hosted version — your choice. - -## What's coming next - -While Standup Kiwi is already helpful in its current form, we're working on some features that will make it even better: - -- **Team boards** — Work together, stay in sync, and build better communication habits as a team -- **AI features** to save you time and effort: - - Listen to audio summaries of team updates while you're on the go - - Get helpful writing suggestions based on your past updates - - Transform quick thoughts into clear, professional standup messages - -We're just getting started, and your feedback will help shape what comes next! - -## You might be wondering… - -- **Is it free?** - Yes, it's completely free right now. During the beta period, I'll be introducing paid plans alongside a free plan with limited usage. Self-hosting will remain free, and I'll ensure it's easy to self-host and migrate your data. - -- **Can I use it with a team?** - Not yet. Team boards are in progress. - -- **Can I self-host it?** - Yes. It's open source and will stay that way. There's no setup guide yet, but one is planned. - -- **Does it work on mobile?** - Yes, the web app is fully responsive and works well on mobile browsers. A native mobile app isn't available yet, but it's on the roadmap. - -## Try it out - -If this sounds useful to you, give it a try: [https://standupkiwi.com](https://app.standupkiwi.com) - -Since it's open source, I'd love your feedback. Found a bug? Have an idea? [Open an issue on GitHub](https://github.com/kiwinight/standup-kiwi) — or just drop a star to support the project. - -I'm already using it every day — and I hope it helps you too. diff --git a/sites/website/src/lib/utils.ts b/sites/website/src/lib/utils.ts deleted file mode 100644 index 8086209..0000000 --- a/sites/website/src/lib/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -type ClassValue = - | string - | number - | boolean - | undefined - | null - | { [key: string]: boolean } - | ClassValue[]; - -export function cn(...inputs: ClassValue[]): string { - const classes: string[] = []; - - for (const input of inputs) { - if (!input) continue; - - if (typeof input === "string" || typeof input === "number") { - classes.push(String(input)); - } else if (typeof input === "object" && !Array.isArray(input)) { - for (const [key, value] of Object.entries(input)) { - if (value) { - classes.push(key); - } - } - } else if (Array.isArray(input)) { - const nested = cn(...input); - if (nested) { - classes.push(nested); - } - } - } - - return classes.join(" "); -} diff --git a/sites/website/src/pages/blog/[...slug]/content.tsx b/sites/website/src/pages/blog/[...slug]/content.tsx deleted file mode 100644 index b3d90c5..0000000 --- a/sites/website/src/pages/blog/[...slug]/content.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { - Box, - Container, - Flex, - Text, - Heading, - Section, - Theme, - Card, - Inset, - Button, - Link, -} from "@radix-ui/themes"; -import { NavBar } from "../../../components/nav-bar"; -import type { RenderedContent } from "astro:content"; -import type { InferEntrySchema } from "astro:content"; -import type { ReactNode } from "react"; -import { ArrowLeftIcon, ExternalLinkIcon } from "@radix-ui/react-icons"; - -type Props = { - post: { - id: string; - body?: string; - collection: "blog"; - data: InferEntrySchema<"blog">; - rendered?: RenderedContent; - filePath?: string; - }; - children: ReactNode; -}; - -function Content({ post, children }: Props) { - return ( - - - -
- - - - - - - - {post.data.title} - - - - Posted on{" "} - {post.data.pubDate.toLocaleDateString("en-US", { - month: "long", - day: "numeric", - year: "numeric", - })}{" "} - {post.data.updatedDate && ( - <> - • Edited on{" "} - {post.data.updatedDate?.toLocaleDateString("en-US", { - month: "long", - day: "numeric", - year: "numeric", - })} - - )}{" "} - - - - {post.data.authorLink ? ( - - {post.data.author} - - ) : ( - post.data.author - )} - {post.data.authorTitle && ( - - {", " + post.data.authorTitle} - - )} - - - - -
- -
- - - - - {post.data.title} - - - {children} - - -
-
-
- ); -} - -export default Content; diff --git a/sites/website/src/pages/blog/[...slug]/index.astro b/sites/website/src/pages/blog/[...slug]/index.astro deleted file mode 100644 index 9560ffc..0000000 --- a/sites/website/src/pages/blog/[...slug]/index.astro +++ /dev/null @@ -1,34 +0,0 @@ ---- -import "@radix-ui/themes/styles.css"; -import "../../../styles/global.css"; -import { type CollectionEntry, getCollection } from "astro:content"; -import { render } from "astro:content"; -import BaseHead from "../../../components/base-head.astro"; -import ContentHead from "../../../components/content-head.astro"; -import Content from "./content"; - -export async function getStaticPaths() { - const posts = await getCollection("blog"); - return posts.map((post) => ({ - params: { slug: post.id }, - props: post, - })); -} -type Props = CollectionEntry<"blog">; - -const post = Astro.props; -const { Content: PostContent } = await render(post); ---- - - - - - - - - - - - - - diff --git a/sites/website/src/pages/blog/constants.ts b/sites/website/src/pages/blog/constants.ts deleted file mode 100644 index fcfeeb4..0000000 --- a/sites/website/src/pages/blog/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const PAGE_DESCRIPTION = - "Thoughts, updates, and helpful tips from the Standup Kiwi team."; diff --git a/sites/website/src/pages/blog/content.tsx b/sites/website/src/pages/blog/content.tsx deleted file mode 100644 index 3b0da75..0000000 --- a/sites/website/src/pages/blog/content.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { - Box, - Card, - Container, - Flex, - Heading, - Inset, - Section, - Text, - Theme, -} from "@radix-ui/themes"; -import { NavBar } from "../../components/nav-bar"; -import type { InferEntrySchema, RenderedContent } from "astro:content"; -import { PAGE_DESCRIPTION } from "./constants"; - -type Props = { - posts: { - id: string; - body?: string; - collection: "blog"; - data: InferEntrySchema<"blog">; - rendered?: RenderedContent; - filePath?: string; - }[]; -}; - -function Content({ posts }: Props) { - return ( - - - -
- - - - - Blog - - - {PAGE_DESCRIPTION} - - - - -
-
- - - {posts.map((post) => { - return ( - - - - {post.data.heroImage && ( - {post.data.title} - )} - - - - {post.data.title} - - - {post.data.description} - - - {post.data.pubDate.toLocaleDateString("en-US", { - month: "long", - day: "numeric", - year: "numeric", - })} - - - - - ); - })} - - -
-
-
- ); -} - -export default Content; diff --git a/sites/website/src/pages/blog/index.astro b/sites/website/src/pages/blog/index.astro deleted file mode 100644 index f542451..0000000 --- a/sites/website/src/pages/blog/index.astro +++ /dev/null @@ -1,24 +0,0 @@ ---- -import "@radix-ui/themes/styles.css"; -import "../../styles/global.css"; -import BaseHead from "../../components/base-head.astro"; -import { getCollection } from "astro:content"; -import ContentHead from "../../components/content-head.astro"; -import Content from "./content"; -import { PAGE_DESCRIPTION } from "./constants"; - -const posts = (await getCollection("blog")).sort( - (a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf() -); ---- - - - - - - - - - - - diff --git a/sites/website/src/pages/index-content.tsx b/sites/website/src/pages/index-content.tsx deleted file mode 100644 index b796af7..0000000 --- a/sites/website/src/pages/index-content.tsx +++ /dev/null @@ -1,1778 +0,0 @@ -import { - Avatar, - Badge, - Box, - Button, - Card, - Container, - Flex, - Grid, - Heading, - Inset, - Section, - Text, - Theme, - Separator, - Link, -} from "@radix-ui/themes"; -import { useEffect, useRef } from "react"; - -// Hook for staggered delays - 시차를 둔 (staggered) animations -function useStaggeredDelay(gap: number = 0.25, startDelay: number = 0) { - const indexRef = useRef(0); - - const getNextDelay = () => { - const delay = startDelay + indexRef.current * gap; - indexRef.current++; - return delay; - }; - - const reset = () => { - indexRef.current = 0; - }; - - return { getNextDelay, reset }; -} - -import howItWorksDemo3By4 from "../assets/overview/how-it-works-1080p-60fps-web-3x4.mp4"; -import howItWorksDemo16By9 from "../assets/overview/how-it-works-1080p-30fps-web-16x9.mp4"; - -import { NavBar } from "../components/nav-bar"; -import { ChevronDownIcon } from "@radix-ui/react-icons"; - -import { ClockFadingIcon, GithubIcon, LayoutGridIcon } from "lucide-react"; - -import { ExternalLinkIcon } from "@radix-ui/react-icons"; -import { Accordion as AccordionPrimitive } from "radix-ui"; -import { BlurFade } from "../components/blur-fade"; -import { ScrollFade } from "../components/scroll-fade"; -import { TextAnimate } from "../components/text-animate"; -import { cn } from "../lib/utils"; -import { LineShadowText } from "../components/line-shadow-text"; - -const DELAY = 0.125; - -function Hero({ - goodVibesGradientImgSlot, - standupKiwiResponsiveMockupImgSlot, -}: { - goodVibesGradientImgSlot: never; - standupKiwiResponsiveMockupImgSlot: never; -}) { - return ( - <> -
- - astro-slot]:w-full [&>astro-slot]:h-full [&>astro-slot>img]:w-full [&>astro-slot>img]:h-full [&>astro-slot>img]:object-cover [&>astro-slot>img]:object-center - `} - > - {goodVibesGradientImgSlot} - - - - -
- - - - - - The standup tool that -
- - {" "} - actually works - -
- - - - - Organized Board - - - - Shorter Meetings - - - - Open Source - - - - Skip the endless meetings and scattered chat threads. -
Standup Kiwi gives you a - simple, organized board to share and record daily updates -
— whether you work solo or - with a team. -
- - -
-
- - - - - astro-slot>img]:w-auto [&>astro-slot>img]:max-h-[520px] [&>astro-slot>img]:max-w-none - sm:[&>astro-slot>img]:w-full sm:[&>astro-slot>img]:max-h-[906px] sm:[&>astro-slot>img]:max-w-full sm:[&>astro-slot>img]:h-auto - `} - > - {standupKiwiResponsiveMockupImgSlot} - - -
-
-
-
- - ); -} - -function Problem() { - const problems = [ - { - title: "Endless meetings...", - description: - "Daily standup meetings that drag on for 30 minutes when everyone just needs to share quick updates.", - }, - { - title: "Lost in Slack...", - description: - "Scattered updates buried in threads that you can never find later when you need them.", - }, - { - title: "Tool overload...", - description: - "Over-engineered tools like Jira, Notion templates, or Linear that feel like overkill for simple daily check-ins.", - }, - { - title: "No record keeping...", - description: - "When your boss asks 'what's your team been working on?' you scramble to remember what happened last week.", - }, - ]; - - return ( -
- - - - - Are you tired of...? - - - - - {problems.map((problem, index) => { - const isLast = index === problems.length - 1; - - return ( - - - - {problem.title} - - {problem.description} - - - ); - })} - - - -
- ); -} - -function MeetStandupKiwi({ - autumnalPeachGradientImgSlot, - standupKiwiBoardDemoImgSlot, -}: { - autumnalPeachGradientImgSlot: never; - standupKiwiBoardDemoImgSlot: never; -}) { - const benefits = [ - { - title: ( - <> - Turn 30-minute meetings -
into focused 5-minute standups - - ), - description: - "Prepare your updates beforehand, then share the board during your standup. Everyone comes prepared, making meetings shorter and more focused. Or skip the meeting entirely. It's your choice.", - }, - { - title: ( - <> - Everything organized in one place -
- - ), - description: - "No more scrolling through weeks of Slack messages to find that one update you need. All your team's progress lives in clean, organized cards with a clear browsable history.", - }, - { - title: ( - <> - Simple and focused - built just for standups -
- - ), - description: - "Unlike Jira, Notion templates, or Linear, Standup Kiwi does one thing really well: daily standups. No complex project management features you don't need, no overwhelming setup process, just clean daily updates.", - }, - { - title: ( - <> - Never lose track of what your team accomplished -
- - ), - description: - "When your boss asks 'what's your team been working on?' you'll have a clear, browsable history instead of scrambling to remember. Perfect for performance reviews, progress reports, and onboarding new team members.", - }, - ]; - - return ( -
- - - - - - Meet Standup Kiwi — -
- Turn dragging meetings into quick sync -
- - Get your time back, keep your team aligned, and always know - what's happening. -
- No more scrambling for answers when your boss asks for updates. -
-
-
- {/* */} - - - - - - - {autumnalPeachGradientImgSlot} - - - - -
- - - - {standupKiwiBoardDemoImgSlot} - - - -
-
-
- {benefits.map((benefit, index) => { - const isLast = index === benefits.length - 1; - - return ( - - - - - {benefit.title} - - {benefit.description} - - - - ); - })} -
-
-
-
- ); -} - -function HowItWorks() { - const mobileVideoRef = useRef(null); - const desktopVideoRef = useRef(null); - - useEffect(() => { - // Apply playback rate to both videos - if (mobileVideoRef.current) { - mobileVideoRef.current.playbackRate = 0.75; - } - if (desktopVideoRef.current) { - desktopVideoRef.current.playbackRate = 0.75; - } - }, []); - - // Mobile video viewport observer - useEffect(() => { - const video = mobileVideoRef.current; - if (!video) return; - - const isVideoVisible = () => window.innerWidth <= 767; - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - video.play().catch((error) => { - console.log("Mobile video autoplay prevented:", error); - }); - } else { - video.pause(); - } - }); - }, - { - threshold: 0.5, - rootMargin: "0px", - } - ); - - // Setup observer and media query listener - if (isVideoVisible()) { - observer.observe(video); - } - - const mediaQuery = window.matchMedia("(max-width: 767px)"); - const handleMediaChange = () => { - if (isVideoVisible()) { - observer.observe(video); - } else { - observer.unobserve(video); - video.pause(); - } - }; - - mediaQuery.addListener(handleMediaChange); - - return () => { - observer.disconnect(); - mediaQuery.removeListener(handleMediaChange); - }; - }, []); - - // Desktop video viewport observer - useEffect(() => { - const video = desktopVideoRef.current; - if (!video) return; - - const isVideoVisible = () => window.innerWidth > 767; - - const observer = new IntersectionObserver( - (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - video.play().catch((error) => { - console.log("Desktop video autoplay prevented:", error); - }); - } else { - video.pause(); - } - }); - }, - { - threshold: 0.5, - rootMargin: "0px", - } - ); - - // Setup observer and media query listener - if (isVideoVisible()) { - observer.observe(video); - } - - const mediaQuery = window.matchMedia("(min-width: 768px)"); - const handleMediaChange = () => { - if (isVideoVisible()) { - observer.observe(video); - } else { - observer.unobserve(video); - video.pause(); - } - }; - - mediaQuery.addListener(handleMediaChange); - - return () => { - observer.disconnect(); - mediaQuery.removeListener(handleMediaChange); - }; - }, []); - - const steps = [ - { - title: "Create your board", - description: - "Set up a team or personal board in seconds. Name it whatever makes sense — 'Daily Progress,' 'Team Alpha,' or just your name. No complex setup.", - }, - { - title: "Write your update", - description: - "Answer the classic questions: What did you do yesterday? What's planned for today? Any blockers? Use our clean markdown editor to format lists, add links, and make your updates scannable.", - }, - { - title: "Stay organized", - description: - "Your updates become organized cards on a clean board. Browse today's updates, review past work, and never lose track of progress again. Everything is right where you need it, when you need it.", - }, - ]; - - return ( -
- - - - - - How it works - - - - Simple as 1-2-3 - - - - - - - - - {/* Mobile video */} - - - - {steps.map((step, index) => { - return ( - - - - - - - {step.title} - - {step.description} - - - - - ); - })} - - - -
- ); -} - -function WhoItsPerfectFor() { - const targets = [ - { - title: "Small teams, startups, distributed companies", - description: ( - <> -
    -
  • - Keep everyone informed without interrupting deep work -
  • - -
  • - Create a browsable history of team progress -
  • - -
  • - Onboard new members with context from day one -
  • - -
  • - - Make time-zone distributed meetings work with prepared updates - -
  • -
- - ), - }, - { - title: "Developers, designers, writers, consultants", - description: ( - <> -
    -
  • - Start each day with clarity and purpose -
  • -
  • - Track your progress over time -
  • -
  • - Never lose track of what you were working on -
  • -
  • - Build a portfolio of your daily achievements -
  • -
- - ), - }, - { - title: "Maintainers, contributors, volunteer teams", - description: ( - <> -
    - {" "} -
  • - Self-host with complete control over your data -
  • -
  • - - Coordinate across continents without scheduling conflicts - -
  • -
  • - Document contributions and progress transparently -
  • -
  • - Keep contributors engaged with visible momentum -
  • -
- - ), - }, - ]; - - return ( -
- - - - - Who it's perfect for - - - - {targets.map((target, index) => { - const isLast = index === targets.length - 1; - return ( - - - - - {target.title} - - {target.description} - - - - ); - })} - - - -
- ); -} - -function OpenSource() { - return ( -
- - - - - Open source you can trust - - - - - - - - - Your Data, Your Rules - -
    -
  • - - Complete transparency - see exactly how your data is - handled - -
  • -
  • - - Self-hosting option - keep everything on your own - servers - -
  • -
  • - No vendor lock-in - export your data anytime -
  • -
  • - - Community-driven - shaped by real users like you - -
  • -
-
-
-
- - - - - Built in the Open - -
    -
  • - - Open source on GitHub - help us build something great - together - -
  • -
  • - Active development with regular updates -
  • -
  • - - Community support for issues and feature requests - -
  • -
  • - Getting started guide with setup instructions -
  • -
- -
-
-
-
-
-
-
- ); -} - -function Pricing() { - return ( -
- - - - - Pricing - - - - - - - - Self-hosting - - - Deploy on your servers. Complete data control, always free - forever. - - - Free - - -
    -
  • - Always free forever -
  • -
  • - Deploy on your own servers -
  • -
  • - Complete control of your data -
  • -
  • - Full feature access -
  • -
- - - -
-
-
- - - - - - Managed service - - - Fully hosted with zero setup. All features included, - automatic updates. - - - - - $2 - - - per user on each board, when paying monthly - - - - - - Free - - - - Limited-time launch offer for early adopters - - - -
    -
  • - Hosted at standupkiwi.com -
  • -
  • - Zero setup required -
  • -
  • - All features included -
  • -
- - -
-
-
-
-
-
-
- ); -} - -const FAQ_ITEMS = [ - { - question: "Is it really free?", - answer: - "Yes! Everything is free right now. Self-hosting will always stay free, and our managed service will introduce pricing much later with advance notice and data migration support.", - }, - { - question: "Can my team use it now?", - answer: - "[TODO: we already have a shared board feature.]Team collaboration features are in beta. Individual team members can create personal boards immediately, and we're rolling out shared team boards soon.", - }, - { - question: "How is this different from just using Slack?", - answer: - "[TODO: this is too much] Slack is great for quick conversations, but ~terrible~ for organized daily updates. Standup Kiwi creates a clean, searchable history of your work that you can actually use.", - }, - { - question: "Do I need to know how to code to self-host?", - answer: - "[TODO: no one click..] Basic server knowledge helps, but we're working on one-click deployment options. Most users are happy with our hosted version.", - }, - { - question: "What if I don't like it?", - answer: - "No problem! Your data is yours—export it anytime. No contracts, no hassle.", - }, -]; - -function FAQ() { - return ( -
- - - - Questions? Answers - - - - {FAQ_ITEMS.map((item, index) => { - return ( - - - - - - {item.question} - - - - - - - {item.answer} - - - - - ); - })} - - - - -
- ); -} - -function Roadmap() { - const itmes = [ - { - title: "Yesterday context recall", - description: - "What did I do yesterday again? Auto-fill from your previous updates", - }, - { - title: "Today suggestion engine", - description: - "Get smart suggestions for today's update based on your work patterns", - }, - { - title: "Daily team digest", - description: - "See what everyone's working on today (not just for managers!)", - }, - { - title: "AI weekly digest", - description: - "Auto-generated personal and team summaries with insights and trends", - }, - { - title: "Desktop and mobile apps", - description: "Native desktop and mobile apps for a seamless experience.", - }, - { - title: "And more...!", - description: - "Your feedback shapes our priorities. What would help you most?", - }, - ]; - - return ( -
- - - - - - What's coming next - - - We're just getting started. Here's what we're working on based - on real user feedback: - - - - - - {itmes.map((item, index) => { - const isHiddenOnMobile = index < 2; - - return ( - - - - - {item.title} - - {item.description} - - - - ); - })} - - - -
- ); -} - -function FinalCTA({ - goodVibesGradientImgSlot, -}: { - goodVibesGradientImgSlot: never; -}) { - return ( -
- - - {goodVibesGradientImgSlot} - - - - -
- - - - - - - Ready to transform your standups? - - - Set up your standup board in seconds - - - - - - - - - - -
-
- ); -} - -function IndexContent(props: any) { - const { - goodVibesGradientImg, - autumnalPeachGradientImg, - standupKiwiResponsiveMockupImg, - standupKiwiBoardDemoImg, - } = props as Record; - - return ( - - - - - - - - - - {/* TODO: Restore FAQ when the contents are ready */} - {/* */} - - - {/* TODO: add footer */} -