Skip to content

PrajwolInCode/SortMyLife

Repository files navigation

SortMyLife

SortMyLife is a calm, local-first productivity dashboard built with React + TypeScript + Vite.

Tech stack

  • Vite
  • React + React Router
  • TypeScript
  • Tailwind CSS
  • Zustand
  • Framer Motion
  • Recharts
  • Netlify Functions
  • localStorage persistence (no backend database)

New Interactive Features Added

SortMyLife now includes a few extra gentle touches to make daily use feel calmer, clearer, and more supportive.

What’s new (and where it lives)

  • Memory Match mini game

    • A simple 4x4 card match game with move tracking and best-score history.
    • Component: src/components/games/MemoryMatchGame.tsx
    • Appears inside Pause Corner on:
      • src/pages/DashboardPage.tsx
      • src/pages/GamesPage.tsx
      • via src/components/pause/PauseCornerSection.tsx
  • Pause Corner

    • A soft “take-a-breath” area with quick reset tools (breathing bubble + Memory Match), plus hide/show controls.
    • Component: src/components/pause/PauseCornerSection.tsx
    • Rendered on dashboard and games pages:
      • src/pages/DashboardPage.tsx
      • src/pages/GamesPage.tsx
  • Engagement cards (“Guidance for you”)

    • Rotating guidance cards with Save / Like / Hide actions and fallback states.
    • Component: src/components/dashboard/GuidanceForYouSection.tsx
    • Added to: src/pages/DashboardPage.tsx
  • Toasts (gentle feedback messages)

    • Lightweight toast notifications for saves, encouragement, and celebration moments.
    • Store: src/store/useToastStore.ts
    • Viewport: src/components/common/ToastViewport.tsx
    • Mounted globally in: src/App.tsx
  • Skeleton loading states

    • Soft skeleton placeholders for loading moments on key screens/cards.
    • Base skeleton: src/components/common/Skeleton.tsx
    • Presets: src/components/common/PageSkeletons.tsx
    • Used in places like:
      • src/components/dashboard/GuidanceForYouSection.tsx
      • src/pages/PeoplePage.tsx
      • src/pages/ProfilePage.tsx
  • Page + component transitions

    • Calm fade/slide transitions across routes, layout content, cards, toasts, and selected mini-game interactions.
    • Key files:
      • src/App.tsx (route transitions)
      • src/components/layout/AppLayout.tsx (page container transitions)
      • src/components/common/Card.tsx (card entrance motion)
      • src/components/common/ToastViewport.tsx (toast enter/exit motion)
      • src/components/games/MemoryMatchGame.tsx (tile feedback + win message motion)

State persistence: local-first vs backend

  • Saved locally (device/browser)

    • Core personal app state is persisted with Zustand + localStorage in src/store/useAppStore.ts (sort-my-life-v3).
    • Pause Corner visibility preference uses useLocalStorage in src/components/pause/PauseCornerSection.tsx.
    • Memory Match last/best scores are saved in localStorage in src/components/games/MemoryMatchGame.tsx.
    • Toasts are intentionally in-memory/temporary for quick feedback (src/store/useToastStore.ts).
  • Saved via backend (when shared features are used)

    • Shared workspace/auth flows use Supabase helpers in src/lib/supabase.ts and related workspace/auth pages.
    • Examples: src/pages/WorkspacePage.tsx, src/pages/TasksPage.tsx, src/pages/HabitsPage.tsx, src/pages/ProfilePage.tsx.
    • Practical model: personal wellbeing flow stays local-first; collaborative/shared data syncs through Supabase.

Animation approach + reduced-motion support

  • Animations are implemented with Framer Motion (motion, AnimatePresence, useReducedMotion) for route changes, cards, toasts, and interactive micro-feedback.
  • If someone prefers reduced motion, components switch to minimal or no movement (for example, opacity-only transitions or disabled hover/tap transforms).
  • CSS also respects reduced-motion for non-Framer effects (like skeleton shimmer and decorative animation) using @media (prefers-reduced-motion: reduce) in src/index.css.

Dependencies added/used

  • No new dependency beyond current stack is required for these additions.
  • Framer Motion is the primary animation dependency used more broadly across the new interactive experiences:
    • package: framer-motion
    • current version in this repo: ^12.23.24 (see package.json)

Run locally

npm install
npm run dev

Open: http://localhost:5173

Netlify local testing (including AI function)

  1. Install Netlify CLI (if needed):
    npm i -g netlify-cli
  2. Create a .env file (or export env var in shell):
    OPENAI_API_KEY=your_key_here
    Or set it in your current shell session:
    export OPENAI_API_KEY="your_key_here"
  3. Run Netlify dev:
    npm run netlify:dev
  4. Open the local URL from Netlify dev and click Plan My Day on the dashboard.

Important: the AI button calls a Netlify Function at /api/plan-my-day, so npm run dev alone will return 404 for that feature.

/api/* is redirected to Netlify Functions in netlify.toml to avoid SPA fallback returning HTML to the AI fetch call.

Build for production

npm run build

Vite output directory: dist/.

If you see a blank page on http://localhost:8888

  1. Check netlify dev terminal: Vite must be running on 5173.
  2. Open http://localhost:5173 directly. If it works there, refresh http://localhost:8888.
  3. If 5173 is busy, stop the conflicting process and restart npm run netlify:dev.
  4. Hard refresh browser (Ctrl+Shift+R).

If you see Failed to load module script / MIME type errors

This app entrypoint is src/main.tsx and must be transformed by Vite. If you open the project with a plain static server (for example python -m http.server, Live Server, or similar), the browser receives .tsx without a JS module MIME type and the page stays blank.

Use one of these instead:

npm run dev
# then open http://localhost:5173

or

npm run netlify:dev
# then open the URL printed by Netlify (often http://localhost:8888)

Quick verification:

  • http://localhost:5173/src/main.tsx should return Content-Type: text/javascript when Vite is running.
  • If that URL is 404 or has an empty/wrong MIME type, restart the Vite/Netlify dev process.

Netlify plugin note (important)

If your Netlify UI still has @netlify/plugin-nextjs enabled from older builds, disable it in: Site settings → Build & deploy → Build plugins.

This repo is a Vite app, not Next.js. netlify.toml also sets NEXT_PLUGIN_FORCE_RUN=false as a safeguard.

AI abuse protection setup

Add these environment variables in Netlify (and locally for netlify dev):

OPENAI_API_KEY=...
TURNSTILE_SECRET_KEY=...
VITE_TURNSTILE_SITE_KEY=...

The AI endpoints enforce:

  • cooldown between requests
  • short-window and daily caps
  • quick verification via Cloudflare Turnstile
  • prompt/input size limits and safe timeouts

Turnstile troubleshooting (Cloudflare + Netlify)

If users see "Unable to connect to website" or repeated verification failures, run this checklist in order:

  1. Check Turnstile widget hostnames in Cloudflare

    • Go to Cloudflare Dashboard → Turnstile → Widgets → (your widget).
    • Under Hostname management, make sure every live hostname is listed (for example: sortmylife.com.au, www.sortmylife.com.au, and any temporary domain in use).
    • Save changes.
  2. Confirm key pairing is correct

    • In Cloudflare, copy the widget Site Key and Secret Key.
    • In Netlify go to Site configuration → Environment variables and confirm:
      • VITE_TURNSTILE_SITE_KEY = Site Key
      • TURNSTILE_SECRET_KEY = Secret Key
    • If you rotate keys, update both values and redeploy.
  3. Set backend hostname allowlist in Netlify

    • In Netlify environment variables, set:
      • TURNSTILE_ALLOWED_HOSTNAMES=sortmylife.com.au,www.sortmylife.com.au,sortmylife.netlify.app
    • Include any additional production/staging hostname users actually open.
  4. Redeploy after env var changes

    • In Netlify, trigger Deploys → Trigger deploy → Deploy site.
    • Environment variable changes are not active until a new deploy finishes.
  5. Verify in browser and in-app browser

    • Test on Safari/Chrome first.
    • If failure only happens inside apps like Instagram/Messenger webviews, ask users to open in Safari/Chrome as a fallback.
  6. Check function logs for verification detail

    • Netlify: Functions → support-chat (or tarot/astrology functions) → Logs.
    • Look for Turnstile verification errors and error codes returned by Cloudflare.

Shared workspace (Supabase) setup

To enable email sign-in, workspaces, email invites, and synced shared entries:

  1. Create a Supabase project.
  2. Run supabase/schema.sql in the Supabase SQL editor.
  3. Add environment variables locally and in Netlify:
VITE_SUPABASE_URL=...
VITE_SUPABASE_ANON_KEY=...
  1. In Supabase Auth settings, set the site URL and redirect URL to your app:
    • Site URL: https://sortmylife.netlify.app
    • Additional Redirect URLs:
      • https://sortmylife.netlify.app/**
      • http://localhost:3000/**

Workspace/auth routes:

  • /workspace → shared workspace home (create space, invite, shared tasks/routines/check-ins)
  • /join?token=... → accepts invite token and joins workspace
  • /auth/confirm → consumes Supabase hash session and continues redirect flow
  • /workspace?workspace=<id> → opens a specific workspace after join flow
  • /login → email + password sign in
  • /signup → email + password sign up with username
  • /forgot-password and /reset-password → password reset flow

The app currently uses Supabase REST endpoints (not Supabase JS auth client):

  • POST /auth/v1/signup for sign up
  • POST /auth/v1/token?grant_type=password for login
  • POST /auth/v1/recover + /reset-password route for password reset

Recommended Supabase Auth redirect settings:

  • Site URL: https://sortmylife.netlify.app
  • Additional Redirect URLs:
    • https://sortmylife.netlify.app/auth/confirm
    • http://localhost:3000/auth/confirm
    • https://sortmylife.netlify.app/join** (invite links)
    • http://localhost:3000/join** (local invite links)

Optional: friend request email notifications

By default, friend requests appear as in-app notifications only. To also send email notifications when a friend request is created:

  1. Add these Netlify environment variables:
    • RESEND_API_KEY (from Resend)
    • RESEND_FROM_EMAIL (verified sender, e.g. SortMyLife <hello@sortmylife.com.au>)
  2. Redeploy the site after adding env vars.
  3. Edit the template in netlify/functions/_shared/emailTemplates.ts (buildFriendRequestEmailTemplate) to customize colors/copy.

Implementation notes:

  • Function endpoint: netlify/functions/friend-request-email.ts
  • Trigger point: sendFriendRequest(...) in src/lib/supabase.ts (non-blocking call so request creation still succeeds if email fails).

Media feed (posts + reactions) setup

Apply migration supabase/migrations/20260413_posts_media_feed.sql.

This migration adds:

  • posts table with media jsonb array, visibility (friends or workspace), timestamps, indexes, and RLS.
  • post_reactions table with one reaction per user per post (like, love, care, haha) and RLS.
  • post-media Supabase storage bucket with a 50MB object limit and MIME allowlist:
    • images: png, jpg/jpeg, webp, heic, heif
    • videos: mp4, webm, mov (video/quicktime)

Client behavior:

  • HEIC/HEIF is converted to JPEG before upload.
  • Images are compressed client-side.
  • Videos can include uploaded poster thumbnails.
  • Instagram/TikTok URLs use an external-open fallback button (not native <video> playback).

About

Clarity and Liffe Purpose

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors