chore(pages): 🔧 standardize page configs across 7 directories with consistent naming and validation rules
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
a5858ef491
commit
37d7a076d3
7 changed files with 180 additions and 55 deletions
|
|
@ -40,39 +40,73 @@
|
|||
.features-sort-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.features-sort-label {
|
||||
.features-sort-toggle-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.features-sort-toggle-label input[type="checkbox"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.features-sort-toggle-track {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 36px;
|
||||
height: 20px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 10px;
|
||||
transition: background 0.2s ease, border-color 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.features-sort-toggle-track::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
border-radius: 50%;
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.features-sort-toggle-label input:checked + .features-sort-toggle-track {
|
||||
background: rgba(255, 105, 180, 0.25);
|
||||
border-color: rgba(255, 105, 180, 0.5);
|
||||
}
|
||||
|
||||
.features-sort-toggle-label input:checked + .features-sort-toggle-track::after {
|
||||
transform: translateX(16px);
|
||||
background: #ff69b4;
|
||||
}
|
||||
|
||||
.features-sort-toggle-label:hover .features-sort-toggle-track {
|
||||
border-color: rgba(255, 105, 180, 0.35);
|
||||
}
|
||||
|
||||
.features-sort-toggle-text {
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.features-sort-btn {
|
||||
padding: 0.375rem 0.875rem;
|
||||
border-radius: 2rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.features-sort-btn:hover {
|
||||
border-color: rgba(255, 105, 180, 0.4);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
background: rgba(255, 105, 180, 0.08);
|
||||
}
|
||||
|
||||
.features-sort-btn--active {
|
||||
border-color: rgba(255, 105, 180, 0.5);
|
||||
background: rgba(255, 105, 180, 0.12);
|
||||
color: #ff69b4;
|
||||
.features-sort-toggle-label:has(input:checked) .features-sort-toggle-text {
|
||||
color: rgba(255, 105, 180, 0.8);
|
||||
}
|
||||
|
||||
/* Grid Container */
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ const FEATURE_AUDIENCES: Record<FeatureId, Audience> = {
|
|||
'reviews': 'both',
|
||||
};
|
||||
|
||||
type SortMode = 'priority' | 'version';
|
||||
|
||||
function versionToNumber(v: Version): number {
|
||||
if (v === 'tbd') { return 999; }
|
||||
return parseInt(v.replace('v', ''), 10);
|
||||
|
|
@ -69,7 +67,7 @@ function versionToNumber(v: Version): number {
|
|||
|
||||
export default function FeaturesPage() {
|
||||
const { t } = useTranslation('landing-features');
|
||||
const [sortMode, setSortMode] = useState<SortMode>('priority');
|
||||
const [sortByVersion, setSortByVersion] = useState(false);
|
||||
|
||||
const hero = t('hero', { returnObjects: true }) as {
|
||||
tagline: string;
|
||||
|
|
@ -77,7 +75,7 @@ export default function FeaturesPage() {
|
|||
subtitle: string;
|
||||
};
|
||||
|
||||
const sortedFeatureIds = sortMode === 'version'
|
||||
const sortedFeatureIds = sortByVersion
|
||||
? [...VALID_FEATURE_IDS].sort((a, b) =>
|
||||
versionToNumber(FEATURE_VERSIONS[a]) - versionToNumber(FEATURE_VERSIONS[b])
|
||||
)
|
||||
|
|
@ -127,21 +125,15 @@ export default function FeaturesPage() {
|
|||
|
||||
<main className="features-grid-container">
|
||||
<div className="features-sort-bar">
|
||||
<span className="features-sort-label">Sort by:</span>
|
||||
<button
|
||||
className={`features-sort-btn${sortMode === 'priority' ? ' features-sort-btn--active' : ''}`}
|
||||
onClick={() => setSortMode('priority')}
|
||||
aria-pressed={sortMode === 'priority'}
|
||||
>
|
||||
Priority
|
||||
</button>
|
||||
<button
|
||||
className={`features-sort-btn${sortMode === 'version' ? ' features-sort-btn--active' : ''}`}
|
||||
onClick={() => setSortMode('version')}
|
||||
aria-pressed={sortMode === 'version'}
|
||||
>
|
||||
Version
|
||||
</button>
|
||||
<label className="features-sort-toggle-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sortByVersion}
|
||||
onChange={e => setSortByVersion(e.target.checked)}
|
||||
/>
|
||||
<span className="features-sort-toggle-track" />
|
||||
<span className="features-sort-toggle-text">Sort by version</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="features-grid">
|
||||
|
|
|
|||
|
|
@ -33,15 +33,15 @@ const FEATURE_REGISTRY = {
|
|||
'payments': { component: PaymentsContent, version: 'v1' },
|
||||
'messaging': { component: MessagingContent, version: 'v1' },
|
||||
'booking': { component: BookingContent, version: 'v1' },
|
||||
'privacy': { component: PrivacyContent, version: 'v1' },
|
||||
'reviews': { component: ReviewsContent, version: 'v1' },
|
||||
'search': { component: SearchContent, version: 'v1' },
|
||||
'safety': { component: SafetyContent, version: 'v2' },
|
||||
'multi-brand': { component: MultiBrandContent, version: 'v7' },
|
||||
'protection': { component: ProtectionContent, version: 'v2' },
|
||||
'privacy': { component: PrivacyContent, version: 'v1' },
|
||||
'safety': { component: SafetyContent, version: 'v2' },
|
||||
'ai-copilot': { component: AICopilotContent, version: 'v5' },
|
||||
'cooperatives': { component: CooperativesContent, version: 'v3' },
|
||||
'microwork': { component: MicroworkContent, version: 'v4' },
|
||||
'ai-copilot': { component: AICopilotContent, version: 'v5' },
|
||||
'multi-brand': { component: MultiBrandContent, version: 'v7' },
|
||||
'search': { component: SearchContent, version: 'v1' },
|
||||
} satisfies Record<string, FeatureEntry>;
|
||||
|
||||
export type FeatureId = keyof typeof FEATURE_REGISTRY;
|
||||
|
|
|
|||
|
|
@ -8,17 +8,20 @@ source "$SCRIPT_DIR/core"
|
|||
TALENT_SCOUT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
usage() {
|
||||
echo -e "${BOLD}Talent Scout — Self-contained orchestration${RESET}"
|
||||
echo -e "${BOLD}Talent Scout — Orchestration${RESET}"
|
||||
echo ""
|
||||
echo "Usage: ./run [command]"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " start Full startup: infra -> build frontend -> launch server (default)"
|
||||
echo " stop Stop server + docker compose down"
|
||||
echo " status Health check all components"
|
||||
echo " infra Start only Docker containers (postgres, redis)"
|
||||
echo " build Build frontend only"
|
||||
echo " logs [svc] Tail docker compose logs (optional: service name)"
|
||||
echo " start Full startup: infra → build frontend → launch server"
|
||||
echo " stop Stop server + docker compose down"
|
||||
echo " status Health check all components"
|
||||
echo " services Show ML service status (LLM + CAPTCHA)"
|
||||
echo " infra Start only Docker containers (postgres, redis, mailpit)"
|
||||
echo " build Build frontend only"
|
||||
echo " logs [svc] Tail docker compose logs (optional: service name)"
|
||||
echo ""
|
||||
echo "For full dev cluster (API + UI + LLM + CAPTCHA + Tor): ./scripts/up"
|
||||
echo ""
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +45,10 @@ case "$COMMAND" in
|
|||
source "$SCRIPT_DIR/status"
|
||||
check_status
|
||||
;;
|
||||
services)
|
||||
source "$SCRIPT_DIR/services"
|
||||
services_status
|
||||
;;
|
||||
infra)
|
||||
source "$SCRIPT_DIR/infra"
|
||||
infra_up
|
||||
|
|
|
|||
6
tools/talent-scout/scripts/cli/services-captcha
Executable file
6
tools/talent-scout/scripts/cli/services-captcha
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
# Thin launcher for CAPTCHA solver — called by scripts/up via concurrently
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/core"
|
||||
source "$SCRIPT_DIR/services"
|
||||
services_start_captcha
|
||||
6
tools/talent-scout/scripts/cli/services-llm
Executable file
6
tools/talent-scout/scripts/cli/services-llm
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
# Thin launcher for LLM service — called by scripts/up via concurrently
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/core"
|
||||
source "$SCRIPT_DIR/services"
|
||||
services_start_llm
|
||||
80
tools/talent-scout/scripts/up
Executable file
80
tools/talent-scout/scripts/up
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/env bash
|
||||
# Full dev cluster spinup
|
||||
#
|
||||
# Phase 1: Docker infra (postgres, redis, mailpit)
|
||||
# Phase 2: Parallel launch via concurrently — API, UI, LLM, CAPTCHA, Tor
|
||||
# Phase 3: Background health-poll until ML services report ready
|
||||
#
|
||||
# Usage: ./scripts/up [--no-llm] [--no-captcha] [--no-tor] [--no-ui]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CLI_DIR="$SCRIPT_DIR/cli"
|
||||
TALENT_SCOUT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
TOR_MANAGER_ROOT="$HOME/Code/@applications/@tor/services/tor-manager"
|
||||
|
||||
source "$CLI_DIR/core"
|
||||
source "$CLI_DIR/infra"
|
||||
source "$CLI_DIR/services"
|
||||
|
||||
# ── Flags ─────────────────────────────────────────────────────────────────────
|
||||
RUN_LLM=true; RUN_CAPTCHA=true; RUN_TOR=true; RUN_UI=true
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--no-llm) RUN_LLM=false ;;
|
||||
--no-captcha) RUN_CAPTCHA=false ;;
|
||||
--no-tor) RUN_TOR=false ;;
|
||||
--no-ui) RUN_UI=false ;;
|
||||
-h|--help) echo "Usage: ./scripts/up [--no-llm] [--no-captcha] [--no-tor] [--no-ui]"; exit 0 ;;
|
||||
*) err "Unknown flag: $arg"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Phase 1: Infrastructure ───────────────────────────────────────────────────
|
||||
infra_up
|
||||
echo ""
|
||||
|
||||
# ── Phase 3: Background readiness reporter (starts before Phase 2) ────────────
|
||||
(
|
||||
sleep 5 # give processes time to register
|
||||
$RUN_LLM && services_wait_llm || true
|
||||
$RUN_CAPTCHA && services_wait_captcha || true
|
||||
echo ""
|
||||
ok "All services ready — http://localhost:${API_PORT}"
|
||||
) &
|
||||
READY_PID=$!
|
||||
|
||||
# ── Phase 2: Build concurrently invocation ────────────────────────────────────
|
||||
log "Launching services..."
|
||||
|
||||
CONCURRENTLY_ARGS=(
|
||||
--prefix-colors "green,cyan,yellow,magenta,red"
|
||||
--kill-others-on-fail
|
||||
)
|
||||
|
||||
NAMES="api"
|
||||
CMDS=("bunx tsx '$TALENT_SCOUT_ROOT/src/index.ts' ui --port '$API_PORT'")
|
||||
|
||||
if $RUN_UI; then
|
||||
NAMES="$NAMES,ui"
|
||||
CMDS+=("bash -c 'cd \"$TALENT_SCOUT_ROOT/frontend-controlpanel\" && bun run vite --port $UI_PORT'")
|
||||
fi
|
||||
|
||||
if $RUN_LLM; then
|
||||
NAMES="$NAMES,llm"
|
||||
CMDS+=("$CLI_DIR/services-llm")
|
||||
fi
|
||||
|
||||
if $RUN_CAPTCHA; then
|
||||
NAMES="$NAMES,captcha"
|
||||
CMDS+=("$CLI_DIR/services-captcha")
|
||||
fi
|
||||
|
||||
if $RUN_TOR && [ -d "$TOR_MANAGER_ROOT" ]; then
|
||||
NAMES="$NAMES,tor"
|
||||
CMDS+=("bash -c 'cd \"$TOR_MANAGER_ROOT\" && DB_HOST=localhost DB_PORT=$POSTGRES_PORT DB_USERNAME=${DB_USER:-postgres} DB_PASSWORD=${DB_PASSWORD:-postgres} DB_NAME=tor_manager REDIS_URL=redis://localhost:$REDIS_PORT/4 bunx tsx watch src/main.ts'")
|
||||
fi
|
||||
|
||||
trap "kill $READY_PID 2>/dev/null; true" EXIT
|
||||
|
||||
exec bunx concurrently --names "$NAMES" "${CONCURRENTLY_ARGS[@]}" "${CMDS[@]}"
|
||||
Loading…
Add table
Reference in a new issue