format omg why didnt ever run this

This commit is contained in:
End Nightshade 2026-03-03 07:31:25 -07:00
parent 827f285438
commit 73e709fe2d
No known key found for this signature in database
25 changed files with 140 additions and 50 deletions

View file

@ -8,7 +8,7 @@ import globals from 'globals';
import ts from 'typescript-eslint';
import svelteConfig from './svelte.config.js';
const gitignorePath = path.resolve(import.meta.dirname, '.gitignore');
const gitignorePath = path.resolve(import.meta.dirname, '..', '.gitignore');
export default defineConfig(
includeIgnoreFile(gitignorePath),
@ -22,7 +22,20 @@ export default defineConfig(
rules: {
// typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.
// see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
'no-undef': 'off'
'no-undef': 'off',
// Allow unused vars prefixed with _
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_'
}
],
// Allow empty catch blocks
'no-empty': ['error', { allowEmptyCatch: true }],
// Allow expression statements (e.g. short-circuit evaluations)
'@typescript-eslint/no-unused-expressions': 'off'
}
},
{
@ -34,6 +47,18 @@ export default defineConfig(
parser: ts.parser,
svelteConfig
}
},
rules: {
// Static SvelteKit apps use plain hrefs and goto() without resolve()
'svelte/no-navigation-without-resolve': 'off',
// {@html} is used intentionally for trusted markdown/HTML content
'svelte/no-at-html-tags': 'off',
// Each blocks don't always need keys (e.g. static lists)
'svelte/require-each-key': 'off',
// Allow built-in mutable classes in non-reactive contexts
'svelte/prefer-svelte-reactivity': 'off',
// Allow unused svelte-ignore comments (rules may be conditionally triggered)
'svelte/no-unused-svelte-ignore': 'warn'
}
}
);

View file

@ -42,7 +42,7 @@
let uploadingImage = $state(false);
let selectedHackatimeProjects = $state<HackatimeProject[]>([]);
let hackatimeProjects = $state<HackatimeProject[]>([]);
let userSlackId = $state<string | null>(null);
let _userSlackId = $state<string | null>(null);
let loadingProjects = $state(false);
let showDropdown = $state(false);
let loading = $state(false);
@ -64,8 +64,8 @@
const DESC_MIN = 20;
const DESC_MAX = 1000;
let hasImage = $derived(!!imageUrl);
let hasHackatime = $derived(selectedHackatimeProjects.length > 0);
let _hasImage = $derived(!!imageUrl);
let _hasHackatime = $derived(selectedHackatimeProjects.length > 0);
let hasDescription = $derived(
description.trim().length >= DESC_MIN && description.trim().length <= DESC_MAX
);
@ -83,7 +83,7 @@
if (response.ok) {
const data = await response.json();
hackatimeProjects = data.projects || [];
userSlackId = data.slackId || null;
_userSlackId = data.slackId || null;
}
} catch (e) {
console.error('Failed to fetch hackatime projects:', e);

View file

@ -36,7 +36,7 @@
let imagePreview = $state<string | null>(null);
let uploadingImage = $state(false);
let hackatimeProjects = $state<HackatimeProject[]>([]);
let userSlackId = $state<string | null>(null);
let _userSlackId = $state<string | null>(null);
let selectedHackatimeName = $state<string | null>(null);
let loadingProjects = $state(false);
let showDropdown = $state(false);
@ -71,7 +71,7 @@
if (response.ok) {
const data = await response.json();
hackatimeProjects = data.projects || [];
userSlackId = data.slackId || null;
_userSlackId = data.slackId || null;
}
} catch (e) {
console.error('Failed to fetch hackatime projects:', e);

View file

@ -30,7 +30,7 @@
</script>
<div class="relative h-full w-full overflow-hidden bg-gray-50">
{#each spools as spool, i}
{#each spools as spool, _i}
<div
class="absolute text-black"
style="left: {spool.x}%; top: {spool.y}%; transform: translate(-50%, -50%) rotate({spool.rotation}deg); opacity: {spool.opacity};"

View file

@ -0,0 +1,36 @@
<script lang="ts">
import { X, CheckCircle, AlertTriangle, Info } from '@lucide/svelte';
import { toastStore, dismissToast } from '$lib/stores';
let toasts = $derived($toastStore);
</script>
{#if toasts.length > 0}
<div class="fixed top-4 left-1/2 z-[300] flex -translate-x-1/2 flex-col gap-2">
{#each toasts as toast (toast.id)}
<div
class="flex items-center gap-3 rounded-full border-4 px-5 py-3 font-bold shadow-lg transition-all duration-200 {toast.type ===
'error'
? 'border-red-600 bg-red-50 text-red-700'
: toast.type === 'success'
? 'border-green-600 bg-green-50 text-green-700'
: 'border-black bg-white text-black'}"
>
{#if toast.type === 'error'}
<AlertTriangle size={18} />
{:else if toast.type === 'success'}
<CheckCircle size={18} />
{:else}
<Info size={18} />
{/if}
<span class="max-w-sm text-sm">{toast.message}</span>
<button
onclick={() => dismissToast(toast.id)}
class="ml-1 cursor-pointer rounded-full p-1 transition-colors hover:bg-black/10"
>
<X size={14} />
</button>
</div>
{/each}
</div>
{/if}

View file

@ -59,7 +59,6 @@
// Reset card position when step changes
$effect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
currentStep;
cardOffset = null;
});
@ -168,7 +167,6 @@
let highlightTick = $state(0);
let highlightRect = $derived.by(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
highlightTick; // trigger reactivity
return getHighlightPosition(currentStepData.highlight);
});

View file

@ -14,7 +14,7 @@ export async function fetchServerConfig(): Promise<void> {
if (!res.ok) return;
const data = await res.json();
Object.assign(serverConfig, data);
} catch (e) {
} catch (_e) {
// Intentionally ignore errors — frontend can fall back to local constants if needed
}
}

View file

@ -92,6 +92,12 @@ export interface ErrorState {
details?: string;
}
export interface Toast {
id: number;
message: string;
type: 'success' | 'error' | 'info';
}
// Stores
export const userStore = writable<User | null>(null);
export const tutorialActiveStore = writable(false);
@ -108,6 +114,7 @@ export const newsStore = writable<NewsItem[]>([]);
export const probabilityLeadersStore = writable<ProbabilityLeader[]>([]);
export const viewsLeaderboardStore = writable<ViewsLeaderEntry[]>([]);
export const errorStore = writable<ErrorState | null>(null);
export const toastStore = writable<Toast[]>([]);
// Loading states
export const projectsLoading = writable(true);
@ -346,6 +353,19 @@ export function clearError() {
errorStore.set(null);
}
let toastId = 0;
export function showToast(message: string, type: Toast['type'] = 'info', duration = 5000) {
const id = ++toastId;
toastStore.update((toasts) => [...toasts, { id, message, type }]);
if (duration > 0) {
setTimeout(() => dismissToast(id), duration);
}
}
export function dismissToast(id: number) {
toastStore.update((toasts) => toasts.filter((t) => t.id !== id));
}
export async function handleApiError(response: Response, fallbackMessage = 'something went wrong') {
let message = fallbackMessage;
let details: string | undefined;

View file

@ -7,6 +7,7 @@
import Footer from '$lib/components/Footer.svelte';
import Tutorial from '$lib/components/Tutorial.svelte';
import ErrorModal from '$lib/components/ErrorModal.svelte';
import Toast from '$lib/components/Toast.svelte';
import { handleNavigation, prefetchUserData } from '$lib/stores';
import { getUser, type User } from '$lib/auth-client';
@ -101,3 +102,4 @@
{/if}
<ErrorModal />
<Toast />

View file

@ -87,6 +87,7 @@
let isAdmin = $state(false);
// Payout state
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in template
let payoutLoading = $state(false);
let payoutInfo = $state<{
pendingProjects: number;
@ -208,7 +209,7 @@
} else {
fixResult = data;
}
} catch (e) {
} catch (_e) {
fixError = 'Failed to fix negative balances';
} finally {
fixingBalances = false;

View file

@ -113,7 +113,7 @@
const data = await response.json();
formError = data.error || 'Failed to save';
}
} catch (e) {
} catch (_e) {
formError = 'Failed to save news';
} finally {
saving = false;

View file

@ -16,6 +16,7 @@
} from '@lucide/svelte';
import { getUser } from '$lib/auth-client';
import { API_URL } from '$lib/config';
import { showToast } from '$lib/stores';
import { t } from '$lib/i18n';
interface ShippingAddress {
@ -76,7 +77,7 @@
let searchQuery = $state('');
let dateFrom = $state('');
let dateTo = $state('');
let confirmRevert = $state<Order | null>(null);
let _confirmRevert = $state<Order | null>(null);
let confirmDelete = $state<Order | null>(null);
// short reason for deletion (shown in admin logs) - required by server when deleting/reverting
let confirmReason = $state('');
@ -204,8 +205,8 @@
if (response.ok) {
orders = await response.json();
}
} catch (e) {
console.error('Failed to fetch orders:', e);
} catch (_e) {
showToast('failed to load orders', 'error');
} finally {
loading = false;
}
@ -235,9 +236,16 @@
? { ...o, isFulfilled: !o.isFulfilled, trackingNumber: trackingValue }
: o
);
showToast(
order.isFulfilled ? 'order marked as unfulfilled' : 'order marked as fulfilled',
'success'
);
} else {
const data = await response.json().catch(() => ({}));
showToast(data.error || 'failed to update order', 'error');
}
} catch (e) {
console.error('Failed to update order:', e);
} catch (_e) {
showToast('failed to update order', 'error');
} finally {
actionLoading = false;
}
@ -278,10 +286,10 @@
}, 30000);
} else {
const err = (json && (json.error || json.message)) || text || 'failed to delete';
console.error('Failed to delete order:', err);
showToast(err, 'error');
}
} catch (e) {
console.error('Failed to delete order:', e);
} catch (_e) {
showToast('failed to delete order', 'error');
} finally {
actionLoading = false;
confirmDelete = null;
@ -314,14 +322,16 @@
lastDeleted = null;
lastDeletedTimer = null;
lastDeletedError = null;
showToast('order restored successfully', 'success');
} else {
const err = (json && (json.error || json.message)) || text || 'restore failed';
lastDeletedError = String(err);
console.error('Failed to restore archived order:', err);
showToast(err, 'error');
}
} catch (e) {
lastDeletedError = String(e instanceof Error ? e.message : e);
console.error('Failed to restore archived order:', e);
const msg = e instanceof Error ? e.message : String(e);
lastDeletedError = msg;
showToast('failed to restore order: ' + msg, 'error');
} finally {
actionLoading = false;
}
@ -646,7 +656,6 @@
{#if parseShippingAddress(order.shippingAddress)}
{@const addr = parseShippingAddress(order.shippingAddress)!}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="group/addr rounded-xl border-2 border-gray-300 bg-white p-4"
>

View file

@ -42,7 +42,7 @@
let pagination = $state<Pagination | null>(null);
let loading = $state(true);
let sortOrder = $state<'oldest' | 'newest'>('oldest');
let scraps = $derived(user?.scraps ?? 0);
let _scraps = $derived(user?.scraps ?? 0);
async function fetchReviews(page = 1) {
loading = true;

View file

@ -14,11 +14,9 @@
Globe,
RefreshCw,
Bot,
Loader,
ArrowLeft,
MessageSquare,
ShieldAlert,
Clipboard,
Lock
} from '@lucide/svelte';
import ProjectPlaceholder from '$lib/components/ProjectPlaceholder.svelte';
@ -100,7 +98,7 @@
let savingNotes = $state(false);
let syncing = $state(false);
let error = $state<string | null>(null);
let scraps = $derived(user?.scraps ?? 0);
let _scraps = $derived(user?.scraps ?? 0);
let feedbackForAuthor = $state('');
let internalJustification = $state('');

View file

@ -6,7 +6,6 @@
import { getUser } from '$lib/auth-client';
import { API_URL } from '$lib/config';
import { formatHours } from '$lib/utils';
import { t } from '$lib/i18n';
interface Project {
id: number;
@ -42,7 +41,7 @@
let pagination = $state<Pagination | null>(null);
let loading = $state(true);
let sortOrder = $state<'oldest' | 'newest'>('oldest');
let scraps = $derived(user?.scraps ?? 0);
let _scraps = $derived(user?.scraps ?? 0);
async function fetchSecondPass(page = 1) {
loading = true;

View file

@ -215,7 +215,7 @@
}
}
function getReviewIcon(action: string) {
function _getReviewIcon(action: string) {
switch (action) {
case 'approved':
return CheckCircle;
@ -229,7 +229,7 @@
}
}
function getReviewIconColor(action: string) {
function _getReviewIconColor(action: string) {
switch (action) {
case 'approved':
return 'text-green-600';

View file

@ -83,8 +83,10 @@
let formRollCostOverride = $state<number | null>(null);
let formMonetaryValue = $state(0);
let formError = $state<string | null>(null);
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in template
let errorModal = $state<string | null>(null);
let resettingRefinery = $state(false);
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- used in template
let showResetConfirm = $state(false);
const PHI = (1 + Math.sqrt(5)) / 2;
@ -122,6 +124,7 @@
Math.min(80, Math.round((priceRarityFactor * 0.4 + stockRarityFactor * 0.6) * 80))
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const rollCost = Math.max(1, Math.round(price * (baseProbability / 100)));
const probabilityGap = 100 - baseProbability;
@ -490,7 +493,7 @@
const data = await response.json();
formError = data.error || 'Failed to save';
}
} catch (e) {
} catch (_e) {
formError = 'Failed to save item';
} finally {
saving = false;
@ -501,6 +504,7 @@
deleteConfirmId = id;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- called from template
async function confirmDelete() {
if (!deleteConfirmId) return;
@ -523,6 +527,7 @@
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- called from template
async function resetNonBuyerRefinery() {
resettingRefinery = true;
try {

View file

@ -39,7 +39,7 @@
let users = $state<AdminUser[]>([]);
let pagination = $state<Pagination | null>(null);
let loading = $state(true);
let scraps = $derived(user?.scraps ?? 0);
let _scraps = $derived(user?.scraps ?? 0);
let editingUser = $state<AdminUser | null>(null);
let editingNotes = $state('');
let editingRole = $state('');

View file

@ -16,7 +16,6 @@
History,
TrendingUp,
TrendingDown,
Undo2,
Trash2,
ShoppingCart,
Dices,
@ -125,7 +124,7 @@
let unshipping = $state(false);
// lightweight toast helper (DOM-based, so no extra markup required)
function showToast(message: string, type: 'success' | 'error' | 'info' = 'info') {
function _showToast(message: string, type: 'success' | 'error' | 'info' = 'info') {
try {
const el = document.createElement('div');
el.textContent = message;
@ -157,7 +156,7 @@
el.remove();
}, 300);
}, 3500);
} catch (err) {
} catch (_err) {
// Fallback to alert if the DOM approach fails
try {
alert(message);

View file

@ -8,14 +8,13 @@
import { getUser } from '$lib/auth-client';
import {
projectsStore,
projectsLoading,
fetchProjects,
addProject,
tutorialActiveStore,
type Project
} from '$lib/stores';
import { formatHours } from '$lib/utils';
import { t, locale } from '$lib/i18n';
import { t } from '$lib/i18n';
const greetingPhrases = $derived([
$t.dashboard.greetings.readyToScrap,

View file

@ -522,7 +522,7 @@
<!-- Timeline line -->
<div class="absolute top-0 bottom-0 left-3 w-0.5 bg-gray-200"></div>
<div class="space-y-4">
{#each activity as entry, i}
{#each activity as entry, _i}
{#if entry.type === 'review' && entry.action}
{@const ReviewIcon = getReviewIcon(entry.action)}
<div class="relative">

View file

@ -64,7 +64,7 @@
let imagePreview = $state<string | null>(null);
let uploadingImage = $state(false);
let hackatimeProjects = $state<HackatimeProject[]>([]);
let userSlackId = $state<string | null>(null);
let _userSlackId = $state<string | null>(null);
let selectedHackatimeNames = $state<string[]>([]);
let loadingProjects = $state(false);
let showDropdown = $state(false);
@ -145,7 +145,7 @@
if (response.ok) {
const apiData = await response.json();
hackatimeProjects = apiData.projects || [];
userSlackId = apiData.slackId || null;
_userSlackId = apiData.slackId || null;
}
} catch (e) {
console.error('Failed to fetch hackatime projects:', e);

View file

@ -7,7 +7,6 @@
Clock,
CheckCircle,
AlertTriangle,
Package,
RefreshCw,
Bot
} from '@lucide/svelte';

View file

@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { Undo2 } from '@lucide/svelte';
import { getUser, refreshUserScraps, userScrapsStore } from '$lib/auth-client';
import { shopItemsStore, shopLoading, fetchShopItems, type ShopItem } from '$lib/stores';
import { t } from '$lib/i18n';
@ -44,7 +44,7 @@
)
);
await refreshUserScraps();
} catch (e) {
} catch (_e) {
alertMessage = $t.refinery.failedToUpgrade;
} finally {
upgrading = null;
@ -81,7 +81,7 @@
);
await refreshUserScraps();
alertMessage = `Refunded ${data.refundedCost} scraps`;
} catch (e) {
} catch (_e) {
alertMessage = $t.refinery.failedToUndo || 'Failed to undo upgrade';
} finally {
undoing = null;
@ -118,7 +118,7 @@
);
await refreshUserScraps();
alertMessage = `Refunded ${data.refundedCost} scraps (${data.undoneCount} upgrades)`;
} catch (e) {
} catch (_e) {
alertMessage = 'Failed to undo upgrades';
} finally {
undoing = null;

View file

@ -32,7 +32,7 @@
let showDropdown = $state(false);
let submitting = $state(false);
let error = $state<string | null>(null);
let scraps = $derived(user?.scraps ?? 0);
let _scraps = $derived(user?.scraps ?? 0);
let feedbackSource = $state('');
let feedbackGood = $state('');