Phase 1 & 2: full-stack family dashboard scaffold
- pnpm monorepo (apps/client + apps/server) - Server: Express + node:sqlite with numbered migration runner, REST API for all 9 features (members, events, chores, shopping, meals, messages, countdowns, photos, settings) - Client: React 18 + Vite + TypeScript + Tailwind + Framer Motion + Zustand - Theme system: dark/light + 5 accent colors, CSS custom properties, anti-FOUC script, ThemeToggle on every surface - AppShell: collapsible sidebar, animated route transitions, mobile drawer - Phase 2 features: Calendar (custom month grid, event chips, add/edit modal), Chores (card grid, complete/reset, member filter, streaks), Shopping (multi-list tabs, animated check-off, quick-add bar, member assign) - Family member CRUD with avatar, color picker - Settings page: theme/accent, photo folder, slideshow, weather, date/time - Docker: multi-stage Dockerfile, docker-compose.yml, entrypoint with PUID/PGID - Unraid: CA XML template, CLI install script, UNRAID.md guide - .gitignore covering node_modules, dist, db files, secrets, build artifacts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
113
unraid/family-planner.xml
Normal file
113
unraid/family-planner.xml
Normal file
@@ -0,0 +1,113 @@
|
||||
<?xml version="1.0"?>
|
||||
<Container version="2">
|
||||
<!--
|
||||
Unraid Community Applications Template
|
||||
Family Planner — self-hosted family dashboard
|
||||
|
||||
To use: place this file in /boot/config/plugins/dockerMan/templates-user/
|
||||
Then open the Docker tab → Add Container and select "Family Planner".
|
||||
-->
|
||||
|
||||
<Name>family-planner</Name>
|
||||
<Repository>ghcr.io/your-username/family-planner:latest</Repository>
|
||||
<Registry>https://ghcr.io/your-username/family-planner</Registry>
|
||||
<Network>bridge</Network>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>false</Privileged>
|
||||
|
||||
<Overview>
|
||||
A sleek, modern family dashboard with a shared calendar, chore assignments,
|
||||
shopping lists, dinner meal planner, message board, countdown timers,
|
||||
and a full-screen photo slideshow screensaver.
|
||||
Includes dark/light mode with accent color selection.
|
||||
</Overview>
|
||||
|
||||
<Category>Productivity: Tools:</Category>
|
||||
<WebUI>http://[IP]:[PORT:3001]/</WebUI>
|
||||
|
||||
<!-- Update this once you have a real icon hosted somewhere -->
|
||||
<Icon>https://raw.githubusercontent.com/your-username/family-planner/main/unraid/icon.png</Icon>
|
||||
|
||||
<ExtraParams>--restart=unless-stopped</ExtraParams>
|
||||
|
||||
<!-- ── Ports ──────────────────────────────────────────────────────── -->
|
||||
<Config
|
||||
Name="Web UI Port"
|
||||
Target="3001"
|
||||
Default="3001"
|
||||
Mode="tcp"
|
||||
Description="Port the Family Planner web interface is served on."
|
||||
Type="Port"
|
||||
Display="always"
|
||||
Required="true"
|
||||
Mask="false">3001</Config>
|
||||
|
||||
<!-- ── Volumes ────────────────────────────────────────────────────── -->
|
||||
<Config
|
||||
Name="App Data"
|
||||
Target="/data"
|
||||
Default="/mnt/user/appdata/family-planner"
|
||||
Mode="rw"
|
||||
Description="Persistent storage for the SQLite database and app configuration. Must be writable."
|
||||
Type="Path"
|
||||
Display="always"
|
||||
Required="true"
|
||||
Mask="false">/mnt/user/appdata/family-planner</Config>
|
||||
|
||||
<Config
|
||||
Name="Photos Path"
|
||||
Target="/photos"
|
||||
Default="/mnt/user/Photos"
|
||||
Mode="ro"
|
||||
Description="Path to your photo library. Subfolders are scanned automatically. Mounted read-only."
|
||||
Type="Path"
|
||||
Display="always"
|
||||
Required="false"
|
||||
Mask="false">/mnt/user/Photos</Config>
|
||||
|
||||
<!-- ── Environment variables ──────────────────────────────────────── -->
|
||||
<Config
|
||||
Name="PUID"
|
||||
Target="PUID"
|
||||
Default="99"
|
||||
Mode=""
|
||||
Description="User ID the container process runs as. Use 'id username' in the Unraid terminal to find your UID. Unraid default nobody=99."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false">99</Config>
|
||||
|
||||
<Config
|
||||
Name="PGID"
|
||||
Target="PGID"
|
||||
Default="100"
|
||||
Mode=""
|
||||
Description="Group ID the container process runs as. Unraid default users=100."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false">100</Config>
|
||||
|
||||
<Config
|
||||
Name="TZ"
|
||||
Target="TZ"
|
||||
Default="America/New_York"
|
||||
Mode=""
|
||||
Description="Your timezone. Used for correct date and time display. See https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false">America/New_York</Config>
|
||||
|
||||
<Config
|
||||
Name="PORT"
|
||||
Target="PORT"
|
||||
Default="3001"
|
||||
Mode=""
|
||||
Description="Internal application port. Only change this if you have a port conflict and know what you are doing."
|
||||
Type="Variable"
|
||||
Display="advanced"
|
||||
Required="false"
|
||||
Mask="false">3001</Config>
|
||||
|
||||
</Container>
|
||||
97
unraid/install.sh
Normal file
97
unraid/install.sh
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Family Planner — Unraid CLI Install Script
|
||||
#
|
||||
# Run from the Unraid terminal:
|
||||
# bash /path/to/install.sh
|
||||
#
|
||||
# Or pipe directly (once the image is published):
|
||||
# curl -fsSL https://raw.githubusercontent.com/your-username/family-planner/main/unraid/install.sh | bash
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configurable defaults (edit these or they will be prompted) ───────────────
|
||||
IMAGE="${IMAGE:-ghcr.io/your-username/family-planner:latest}"
|
||||
CONTAINER_NAME="${CONTAINER_NAME:-family-planner}"
|
||||
HOST_PORT="${HOST_PORT:-3001}"
|
||||
DATA_PATH="${DATA_PATH:-/mnt/user/appdata/family-planner}"
|
||||
PHOTOS_PATH="${PHOTOS_PATH:-/mnt/user/Photos}"
|
||||
PUID="${PUID:-99}"
|
||||
PGID="${PGID:-100}"
|
||||
TZ="${TZ:-America/New_York}"
|
||||
|
||||
# ── Helper ────────────────────────────────────────────────────────────────────
|
||||
info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||
success() { echo -e "\033[1;32m[OK]\033[0m $*"; }
|
||||
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
||||
error() { echo -e "\033[1;31m[ERROR]\033[0m $*" >&2; exit 1; }
|
||||
|
||||
# ── Preflight ─────────────────────────────────────────────────────────────────
|
||||
command -v docker &>/dev/null || error "Docker is not installed or not in PATH."
|
||||
|
||||
info "Family Planner — Unraid Installer"
|
||||
echo ""
|
||||
echo " Container : $CONTAINER_NAME"
|
||||
echo " Image : $IMAGE"
|
||||
echo " Port : $HOST_PORT → 3001"
|
||||
echo " Data path : $DATA_PATH"
|
||||
echo " Photos : $PHOTOS_PATH (read-only)"
|
||||
echo " PUID/PGID : $PUID/$PGID"
|
||||
echo " TZ : $TZ"
|
||||
echo ""
|
||||
read -rp "Proceed with these settings? [Y/n] " confirm
|
||||
confirm="${confirm:-Y}"
|
||||
[[ "$confirm" =~ ^[Yy]$ ]] || { info "Aborted."; exit 0; }
|
||||
|
||||
# ── Stop and remove existing container if present ─────────────────────────────
|
||||
if docker inspect "$CONTAINER_NAME" &>/dev/null; then
|
||||
warn "Container '$CONTAINER_NAME' already exists — stopping and removing it."
|
||||
docker stop "$CONTAINER_NAME" &>/dev/null || true
|
||||
docker rm "$CONTAINER_NAME" &>/dev/null || true
|
||||
fi
|
||||
|
||||
# ── Ensure data directory exists ──────────────────────────────────────────────
|
||||
mkdir -p "$DATA_PATH"
|
||||
success "Data directory ready: $DATA_PATH"
|
||||
|
||||
# ── Pull the latest image ─────────────────────────────────────────────────────
|
||||
info "Pulling image: $IMAGE"
|
||||
docker pull "$IMAGE"
|
||||
|
||||
# ── Run the container ─────────────────────────────────────────────────────────
|
||||
info "Starting container..."
|
||||
docker run -d \
|
||||
--name "$CONTAINER_NAME" \
|
||||
--restart unless-stopped \
|
||||
-p "${HOST_PORT}:3001" \
|
||||
-v "${DATA_PATH}:/data" \
|
||||
-v "${PHOTOS_PATH}:/photos:ro" \
|
||||
-e "PUID=${PUID}" \
|
||||
-e "PGID=${PGID}" \
|
||||
-e "TZ=${TZ}" \
|
||||
-e "PORT=3001" \
|
||||
-e "DATA_DIR=/data" \
|
||||
-e "PHOTOS_DIR=/photos" \
|
||||
"$IMAGE"
|
||||
|
||||
# ── Verify startup ────────────────────────────────────────────────────────────
|
||||
info "Waiting for container to become healthy..."
|
||||
for i in $(seq 1 15); do
|
||||
if docker exec "$CONTAINER_NAME" wget -qO- http://localhost:3001/api/settings &>/dev/null; then
|
||||
success "Family Planner is up!"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
if [ "$i" -eq 15 ]; then
|
||||
warn "Health check timed out. Check logs with: docker logs $CONTAINER_NAME"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
success "Installation complete."
|
||||
echo ""
|
||||
echo " Open in browser : http://$(hostname -I | awk '{print $1}'):${HOST_PORT}"
|
||||
echo " View logs : docker logs -f $CONTAINER_NAME"
|
||||
echo " Stop : docker stop $CONTAINER_NAME"
|
||||
echo " Update image : docker pull $IMAGE && docker stop $CONTAINER_NAME && bash $(realpath "$0")"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user