authentication fix
This commit is contained in:
@@ -1,3 +1,25 @@
|
||||
# 🚀 TasGrid Server Management
|
||||
|
||||
This project uses **PM2** (via Bun) to stay online 24/7. The app runs on port `4000` and is managed by the scripts in `package.json`.
|
||||
|
||||
## 🛠 Quick Commands
|
||||
|
||||
| Action | Command | Description |
|
||||
| :--- | :--- | :--- |
|
||||
| **Check Status** | `bun status` | See if the site is online/how much memory it's using. |
|
||||
| **Update Site** | `bun deploy` | Pulls latest code, builds, and restarts the server. |
|
||||
| **Stop Server** | `bun stop` | Shuts down the site (use for maintenance). |
|
||||
| **View Logs** | `bun logs` | See real-time errors or console output (Ctrl+C to exit). |
|
||||
|
||||
## 🔄 Deployment Workflow
|
||||
To push new changes live, simply run:
|
||||
```bash
|
||||
bun deploy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
|
||||
+8
-2
@@ -4,9 +4,15 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0 --port 4000",
|
||||
"dev": "vite --host 0.0.0.0 --port 4001",
|
||||
"build": "tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
"serve": "vite preview --host 0.0.0.0 --port 4000",
|
||||
"---PM2 COMMANDS---": "------------------",
|
||||
"status": "bun $(command -v pm2) status",
|
||||
"logs": "bun $(command -v pm2) logs tasgrid",
|
||||
"stop": "bun $(command -v pm2) stop tasgrid",
|
||||
"start-prod": "bun $(command -v pm2) start \"bun run serve\" --name \"tasgrid\"",
|
||||
"deploy": "git pull && bun run build && bun $(command -v pm2) restart tasgrid"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kobalte/core": "^0.13.11",
|
||||
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.solid:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
@@ -34,7 +34,10 @@ export const AuthCallback = (props: { onSuccess: () => void }) => {
|
||||
<form onSubmit={handleLogin} class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
placeholder="Email"
|
||||
value={email()}
|
||||
onInput={(e) => setEmail(e.currentTarget.value)}
|
||||
@@ -44,7 +47,10 @@ export const AuthCallback = (props: { onSuccess: () => void }) => {
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
placeholder="Password"
|
||||
value={password()}
|
||||
onInput={(e) => setPassword(e.currentTarget.value)}
|
||||
|
||||
@@ -61,10 +61,12 @@ export const Layout: Component<LayoutProps> = (props) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<main class="flex-1 overflow-auto p-4 md:p-8 pt-4 pb-20 md:pb-8 scroll-smooth relative z-0">
|
||||
{/* Main Content Area */}
|
||||
<main class="flex-1 overflow-y-auto overflow-x-hidden p-4 sm:p-6 lg:p-8 pb-32 sm:pb-6">
|
||||
<div class="max-w-5xl mx-auto w-full">
|
||||
{props.children}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Mobile Bottom Nav */}
|
||||
<BottomNav currentView={props.currentView} setView={props.setView} />
|
||||
</div>
|
||||
|
||||
@@ -154,6 +154,8 @@ export const QuickEntry: Component = () => {
|
||||
<Search class="text-muted-foreground mr-3" size={20} />
|
||||
<TextField class="flex-1">
|
||||
<TextFieldInput
|
||||
id="quick-entry-input"
|
||||
name="task-title"
|
||||
ref={inputRef}
|
||||
value={input()}
|
||||
onInput={(e) => setInput(e.currentTarget.value)}
|
||||
|
||||
@@ -78,6 +78,8 @@ export const TaskDetail: Component<TaskDetailProps> = (props) => {
|
||||
{/* Header Area */}
|
||||
<div class="px-6 pt-6 pb-2 shrink-0">
|
||||
<textarea
|
||||
id="task-detail-title"
|
||||
name="task-title"
|
||||
value={title()}
|
||||
onInput={(e) => {
|
||||
updateTitle(e.currentTarget.value);
|
||||
@@ -160,6 +162,8 @@ export const TaskDetail: Component<TaskDetailProps> = (props) => {
|
||||
<div class="flex items-center gap-1 text-muted-foreground group relative shrink-0 min-w-0 flex-1 sm:flex-initial">
|
||||
<Calendar size={14} class="group-hover:text-primary transition-colors shrink-0" />
|
||||
<input
|
||||
id="task-detail-date"
|
||||
name="due-date"
|
||||
type="datetime-local"
|
||||
value={dateString()}
|
||||
onInput={onDateInputChange}
|
||||
|
||||
+26
-18
@@ -3,7 +3,10 @@ import { createSignal, createEffect, createRoot } from "solid-js";
|
||||
import { pb, TASGRID_COLLECTION } from "@/lib/pocketbase";
|
||||
import { toast } from "solid-sonner";
|
||||
|
||||
const STORAGE_KEY = "tasgrid_data_v1";
|
||||
const getStorageKey = () => {
|
||||
const userId = pb.authStore.model?.id;
|
||||
return userId ? `tasgrid_data_${userId}` : null;
|
||||
};
|
||||
|
||||
export const [now, setNow] = createSignal(Date.now());
|
||||
export const [activeTaskId, setActiveTaskId] = createSignal<string | null>(null);
|
||||
@@ -30,30 +33,21 @@ interface TaskStore {
|
||||
prefId?: string; // ID of the user_preferences record
|
||||
}
|
||||
|
||||
// Synchronous hydration from localStorage for "instant" first paint
|
||||
const getInitialState = (): TaskStore => {
|
||||
const saved = localStorage.getItem(STORAGE_KEY);
|
||||
if (saved) {
|
||||
try {
|
||||
return JSON.parse(saved);
|
||||
} catch (e) {
|
||||
console.error("Failed to parse cached data", e);
|
||||
}
|
||||
}
|
||||
return {
|
||||
// Initial empty state
|
||||
export const [store, setStore] = createStore<TaskStore>({
|
||||
tasks: [],
|
||||
pWeight: 1.0,
|
||||
uWeight: 1.0,
|
||||
matrixScaleDays: 30,
|
||||
};
|
||||
};
|
||||
|
||||
export const [store, setStore] = createStore<TaskStore>(getInitialState());
|
||||
});
|
||||
|
||||
// Auto-persist changes to localStorage (wrapped in root to avoid console warning)
|
||||
createRoot(() => {
|
||||
createEffect(() => {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(store));
|
||||
const key = getStorageKey();
|
||||
if (key) {
|
||||
localStorage.setItem(key, JSON.stringify(store));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,6 +94,19 @@ let heartbeatInterval: number | undefined;
|
||||
export const initStore = async () => {
|
||||
if (!pb.authStore.isValid) return;
|
||||
|
||||
// 0. Synchronous hydration from localStorage for "instant" first paint
|
||||
const key = getStorageKey();
|
||||
if (key) {
|
||||
const saved = localStorage.getItem(key);
|
||||
if (saved) {
|
||||
try {
|
||||
setStore(JSON.parse(saved));
|
||||
} catch (e) {
|
||||
console.error("Failed to parse cached data", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Heartbeat - Ensure only one interval is running
|
||||
if (heartbeatInterval) clearInterval(heartbeatInterval);
|
||||
heartbeatInterval = setInterval(() => setNow(Date.now()), 60000);
|
||||
@@ -127,8 +134,9 @@ export const initStore = async () => {
|
||||
console.warn("Failed to load preferences from user profile:", prefErr);
|
||||
}
|
||||
|
||||
// 2. Fetch Tasks
|
||||
// 2. Fetch Tasks - Explicitly filtered by user and sorted
|
||||
const records = await pb.collection(TASGRID_COLLECTION).getFullList({
|
||||
filter: `user = "${pb.authStore.model?.id}"`,
|
||||
sort: '-created',
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user