This commit is contained in:
parent
622bf8eb0d
commit
45267c7e4e
6 changed files with 37 additions and 51 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "pdsmanager",
|
"name": "pdsmanager",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.3.0",
|
"version": "0.3.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
|
|
|
@ -57,8 +57,8 @@ export default function AuthWrapper() {
|
||||||
<RefreshContext.Provider value={{ refresh, lastUpdate }}>
|
<RefreshContext.Provider value={{ refresh, lastUpdate }}>
|
||||||
<div className="w-full flex flex-col">
|
<div className="w-full flex flex-col">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="min-h-[calc(100vh-64px)] p-4">
|
<div className="min-h-[calc(100vh-64px)] p-2 sm:p-4">
|
||||||
<div className="container mx-auto">
|
<div className="container mx-auto max-w-full">
|
||||||
<TabView />
|
<TabView />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -185,12 +185,12 @@ export default function InviteCodes() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body p-2 sm:p-8">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 sm:gap-4 mb-4">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-4">
|
||||||
<h1 className="card-title text-2xl">Invite Codes</h1>
|
<h1 className="card-title text-xl sm:text-2xl">Invite Codes</h1>
|
||||||
<label className="cursor-pointer label gap-2">
|
<label className="cursor-pointer label gap-2">
|
||||||
<span className="label-text">Show Disabled Codes</span>
|
<span className="label-text">Show Disabled</span>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="toggle toggle-sm toggle-primary"
|
className="toggle toggle-sm toggle-primary"
|
||||||
|
@ -202,7 +202,7 @@ export default function InviteCodes() {
|
||||||
<button
|
<button
|
||||||
onClick={createCode}
|
onClick={createCode}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="btn btn-primary"
|
className="btn btn-primary w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<span className="loading loading-spinner loading-sm"></span>
|
<span className="loading loading-spinner loading-sm"></span>
|
||||||
|
@ -249,21 +249,18 @@ export default function InviteCodes() {
|
||||||
className={`card ${code.disabled ? "bg-base-300 opacity-75" : "bg-base-200"
|
className={`card ${code.disabled ? "bg-base-300 opacity-75" : "bg-base-200"
|
||||||
} hover:shadow-md transition-shadow`}
|
} hover:shadow-md transition-shadow`}
|
||||||
>
|
>
|
||||||
<div className="card-body">
|
<div className="card-body p-3 sm:p-6">
|
||||||
<div className="flex justify-between items-start">
|
<div className="flex flex-col sm:flex-row justify-between items-start gap-4">
|
||||||
<div
|
<div className="font-mono text-base sm:text-lg bg-base-100 p-2 sm:p-3 rounded-box select-all w-full sm:flex-1 break-all">
|
||||||
className={`font-mono text-lg bg-base-100 p-3 rounded-box select-all flex-1 mr-4 ${code.disabled ? "opacity-50" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{code.code}
|
{code.code}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-end gap-2">
|
<div className="flex flex-row sm:flex-col items-center sm:items-end gap-2 w-full sm:w-auto">
|
||||||
{code.disabled ? (
|
{code.disabled ? (
|
||||||
<span className="badge badge-error">Disabled</span>
|
<span className="badge badge-error w-full sm:w-auto justify-center">Disabled</span>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => disableCode(code.code)}
|
onClick={() => disableCode(code.code)}
|
||||||
className="btn btn-sm btn-warning"
|
className="btn btn-sm btn-warning w-full sm:w-auto"
|
||||||
title="Disable code"
|
title="Disable code"
|
||||||
>
|
>
|
||||||
Disable
|
Disable
|
||||||
|
@ -271,7 +268,7 @@ export default function InviteCodes() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-4 mt-2 text-sm">
|
<div className="flex flex-wrap gap-2 mt-2 text-sm">
|
||||||
{!code.disabled && (
|
{!code.disabled && (
|
||||||
<div className="badge badge-neutral">
|
<div className="badge badge-neutral">
|
||||||
Uses: {code.uses.length} / {code.available}
|
Uses: {code.uses.length} / {code.available}
|
||||||
|
@ -282,16 +279,12 @@ export default function InviteCodes() {
|
||||||
</div>
|
</div>
|
||||||
{code.uses.length > 0 && (
|
{code.uses.length > 0 && (
|
||||||
<div className="badge badge-info">
|
<div className="badge badge-info">
|
||||||
{code.uses.length}{" "}
|
{code.uses.length} {code.uses.length === 1 ? "use" : "uses"}
|
||||||
{code.uses.length === 1 ? "use" : "uses"}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{code.uses.length > 0 && (
|
{code.uses.length > 0 && (
|
||||||
<div
|
<div className="text-sm mt-2 break-all">
|
||||||
className={`text-sm mt-2 ${code.disabled ? "opacity-75" : "opacity-70"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span className="font-semibold">Used by:</span>{" "}
|
<span className="font-semibold">Used by:</span>{" "}
|
||||||
{code.uses.map((use) => use.usedBy).join(", ")}
|
{code.uses.map((use) => use.usedBy).join(", ")}
|
||||||
</div>
|
</div>
|
||||||
|
@ -299,11 +292,9 @@ export default function InviteCodes() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{!loading && codes.length === 0 && (
|
{!loading && filterAndSortCodes(codes).length === 0 && (
|
||||||
<div className="text-center p-8 bg-base-200 rounded-box">
|
<div className="text-center p-8 bg-base-200 rounded-box">
|
||||||
<div className="text-base-content/70">
|
<div className="text-base-content/70">No invite codes found</div>
|
||||||
No invite codes found
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -120,7 +120,6 @@ export default function UserList() {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
};
|
};
|
||||||
|
|
||||||
// First get the account info which includes email
|
|
||||||
const accountResponse = await fetch(
|
const accountResponse = await fetch(
|
||||||
`${baseUrl}/xrpc/com.atproto.admin.getAccountInfo?did=${did}`,
|
`${baseUrl}/xrpc/com.atproto.admin.getAccountInfo?did=${did}`,
|
||||||
{ headers }
|
{ headers }
|
||||||
|
@ -132,7 +131,6 @@ export default function UserList() {
|
||||||
|
|
||||||
const accountData = await accountResponse.json();
|
const accountData = await accountResponse.json();
|
||||||
|
|
||||||
// Then get the profile data as before
|
|
||||||
const profileResponse = await fetch(
|
const profileResponse = await fetch(
|
||||||
`${baseUrl}/xrpc/com.atproto.repo.getRecord?collection=app.bsky.actor.profile&repo=${did}&rkey=self`,
|
`${baseUrl}/xrpc/com.atproto.repo.getRecord?collection=app.bsky.actor.profile&repo=${did}&rkey=self`,
|
||||||
{ headers }
|
{ headers }
|
||||||
|
@ -146,7 +144,7 @@ export default function UserList() {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
did,
|
did,
|
||||||
email: accountData.email, // Add email from account info
|
email: accountData.email,
|
||||||
handle: profileData.value.handle,
|
handle: profileData.value.handle,
|
||||||
displayName: profileData.value.displayName,
|
displayName: profileData.value.displayName,
|
||||||
description: profileData.value.description,
|
description: profileData.value.description,
|
||||||
|
@ -284,7 +282,7 @@ export default function UserList() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchUsers(); // Refresh the user list after successful update
|
await fetchUsers();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error("Update handle error:", err);
|
console.error("Update handle error:", err);
|
||||||
setError(err instanceof Error ? err.message : "Failed to update handle");
|
setError(err instanceof Error ? err.message : "Failed to update handle");
|
||||||
|
@ -418,7 +416,7 @@ export default function UserList() {
|
||||||
if (!settingsLoaded) {
|
if (!settingsLoaded) {
|
||||||
return (
|
return (
|
||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body p-2 sm:p-8">
|
||||||
<div className="flex justify-center p-8">
|
<div className="flex justify-center p-8">
|
||||||
<span className="loading loading-spinner loading-lg"></span>
|
<span className="loading loading-spinner loading-lg"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -430,7 +428,7 @@ export default function UserList() {
|
||||||
if (!hasValidSettings) {
|
if (!hasValidSettings) {
|
||||||
return (
|
return (
|
||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body p-2 sm:p-8">
|
||||||
<div className="alert alert-warning">
|
<div className="alert alert-warning">
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
@ -459,13 +457,13 @@ export default function UserList() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card bg-base-100 shadow-xl">
|
<div className="card bg-base-100 shadow-xl">
|
||||||
<div className="card-body">
|
<div className="card-body p-2 sm:p-8">
|
||||||
<div className="flex justify-between items-center mb-4">
|
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-2 sm:gap-4 mb-4">
|
||||||
<h1 className="card-title text-2xl">Users</h1>
|
<h1 className="card-title text-xl sm:text-2xl">Users</h1>
|
||||||
<button
|
<button
|
||||||
onClick={() => fetchUsers()}
|
onClick={() => fetchUsers()}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="btn btn-primary"
|
className="btn btn-primary w-full sm:w-auto"
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<span className="loading loading-spinner loading-sm"></span>
|
<span className="loading loading-spinner loading-sm"></span>
|
||||||
|
@ -708,17 +706,17 @@ export default function UserList() {
|
||||||
key={user.did}
|
key={user.did}
|
||||||
className="card bg-base-200 hover:shadow-md transition-shadow"
|
className="card bg-base-200 hover:shadow-md transition-shadow"
|
||||||
>
|
>
|
||||||
<div className="card-body">
|
<div className="card-body p-3 sm:p-6">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex flex-col sm:flex-row items-start gap-4">
|
||||||
<UserAvatar user={user} />
|
<UserAvatar user={user} />
|
||||||
<div className="flex-1">
|
<div className="flex-1 w-full">
|
||||||
<h2 className="text-lg font-semibold">
|
<h2 className="text-lg font-semibold break-all">
|
||||||
{user.displayName || "Anonymous"}
|
{user.displayName || "Anonymous"}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="font-mono text-sm opacity-70 mt-1">
|
<div className="font-mono text-sm opacity-70 mt-1 break-all">
|
||||||
{user.handle}
|
{user.handle}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-mono text-sm opacity-70 mt-1">
|
<div className="font-mono text-sm opacity-70 mt-1 break-all">
|
||||||
{user.did}
|
{user.did}
|
||||||
</div>
|
</div>
|
||||||
{user.description && (
|
{user.description && (
|
||||||
|
@ -727,8 +725,7 @@ export default function UserList() {
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-1 gap-2 mt-4 sm:mt-0">
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<button
|
<button
|
||||||
onClick={() => setEditModal({
|
onClick={() => setEditModal({
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -793,4 +790,5 @@ export default function UserList() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import Footer from "../components/navigation/Footer.astro";
|
||||||
<html data-theme="dark">
|
<html data-theme="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
rel="icon"
|
||||||
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🦋</text></svg>"
|
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🦋</text></svg>"
|
||||||
|
|
|
@ -26,7 +26,6 @@ export class Settings {
|
||||||
);
|
);
|
||||||
resolve(key);
|
resolve(key);
|
||||||
} else {
|
} else {
|
||||||
// Generate new key if none exists
|
|
||||||
const key = await window.crypto.subtle.generateKey(
|
const key = await window.crypto.subtle.generateKey(
|
||||||
{
|
{
|
||||||
name: "AES-GCM",
|
name: "AES-GCM",
|
||||||
|
@ -36,13 +35,11 @@ export class Settings {
|
||||||
["encrypt", "decrypt"],
|
["encrypt", "decrypt"],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Export and store the key
|
|
||||||
const exportedKey = await window.crypto.subtle.exportKey(
|
const exportedKey = await window.crypto.subtle.exportKey(
|
||||||
"raw",
|
"raw",
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create a new transaction for storing the key
|
|
||||||
const newTransaction = db.transaction(this.STORE_NAME, "readwrite");
|
const newTransaction = db.transaction(this.STORE_NAME, "readwrite");
|
||||||
const newStore = newTransaction.objectStore(this.STORE_NAME);
|
const newStore = newTransaction.objectStore(this.STORE_NAME);
|
||||||
newStore.put(exportedKey, this.ENCRYPTION_KEY_NAME);
|
newStore.put(exportedKey, this.ENCRYPTION_KEY_NAME);
|
||||||
|
|
Loading…
Add table
Reference in a new issue