Add pending apps and frontend components
- apps/captain-mobile: Mobile API service - apps/flow-ui: Flow UI application - apps/mindlink: Mindlink application - apps/storage: Storage API and workers - apps/tzzr-cli: TZZR CLI tool - deck-frontend/backups: Historical TypeScript versions - hst-frontend: Standalone HST frontend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
14
deck-frontend/backups/20260113_212146/src/utils/clipboard.ts
Normal file
14
deck-frontend/backups/20260113_212146/src/utils/clipboard.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { toast } from './toast.ts';
|
||||
|
||||
export async function copyToClipboard(text: string, message?: string): Promise<void> {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
toast(message || 'Copiado');
|
||||
} catch {
|
||||
toast('Error al copiar');
|
||||
}
|
||||
}
|
||||
|
||||
export function copyMrf(mrf: string): void {
|
||||
copyToClipboard(mrf, `MRF copiado: ${mrf.slice(0, 8)}...`);
|
||||
}
|
||||
44
deck-frontend/backups/20260113_212146/src/utils/dom.ts
Normal file
44
deck-frontend/backups/20260113_212146/src/utils/dom.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export const $ = <T extends HTMLElement>(
|
||||
selector: string,
|
||||
parent: ParentNode = document
|
||||
): T | null => parent.querySelector<T>(selector);
|
||||
|
||||
export const $$ = <T extends HTMLElement>(
|
||||
selector: string,
|
||||
parent: ParentNode = document
|
||||
): T[] => Array.from(parent.querySelectorAll<T>(selector));
|
||||
|
||||
export function createElement<K extends keyof HTMLElementTagNameMap>(
|
||||
tag: K,
|
||||
attrs?: Record<string, string>,
|
||||
children?: (HTMLElement | string)[]
|
||||
): HTMLElementTagNameMap[K] {
|
||||
const el = document.createElement(tag);
|
||||
if (attrs) {
|
||||
Object.entries(attrs).forEach(([key, value]) => {
|
||||
if (key === 'className') el.className = value;
|
||||
else if (key.startsWith('data-')) el.setAttribute(key, value);
|
||||
else el.setAttribute(key, value);
|
||||
});
|
||||
}
|
||||
if (children) {
|
||||
children.forEach(child => {
|
||||
el.append(typeof child === 'string' ? child : child);
|
||||
});
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
export function delegateEvent<T extends Event>(
|
||||
container: HTMLElement,
|
||||
selector: string,
|
||||
eventType: string,
|
||||
handler: (event: T, target: HTMLElement) => void
|
||||
): void {
|
||||
container.addEventListener(eventType, (event) => {
|
||||
const target = (event.target as HTMLElement).closest<HTMLElement>(selector);
|
||||
if (target && container.contains(target)) {
|
||||
handler(event as T, target);
|
||||
}
|
||||
});
|
||||
}
|
||||
39
deck-frontend/backups/20260113_212146/src/utils/filters.ts
Normal file
39
deck-frontend/backups/20260113_212146/src/utils/filters.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { Tag, LangType } from '@/types/index.ts';
|
||||
import { getName } from './i18n.ts';
|
||||
|
||||
export interface FilterOptions {
|
||||
search: string;
|
||||
group: string;
|
||||
library: string;
|
||||
libraryMembers: Set<string>;
|
||||
lang: LangType;
|
||||
}
|
||||
|
||||
export function filterTags(tags: Tag[], options: FilterOptions): Tag[] {
|
||||
const { search, group, library, libraryMembers, lang } = options;
|
||||
const q = search.toLowerCase();
|
||||
|
||||
return tags.filter(tag => {
|
||||
// Library filter
|
||||
if (library !== 'all' && !libraryMembers.has(tag.mrf)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Group filter
|
||||
if (group !== 'all' && tag.set_hst !== group) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search filter
|
||||
if (q) {
|
||||
const name = getName(tag, lang).toLowerCase();
|
||||
const ref = (tag.ref || '').toLowerCase();
|
||||
const alias = (tag.alias || '').toLowerCase();
|
||||
if (!name.includes(q) && !ref.includes(q) && !alias.includes(q)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
42
deck-frontend/backups/20260113_212146/src/utils/i18n.ts
Normal file
42
deck-frontend/backups/20260113_212146/src/utils/i18n.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import type { Tag, LangType } from '@/types/index.ts';
|
||||
|
||||
export function getName(tag: Tag, lang: LangType): string {
|
||||
if (lang === 'es' && tag.name_es) return tag.name_es;
|
||||
if (lang === 'en' && tag.name_en) return tag.name_en;
|
||||
if (lang === 'ch' && tag.name_ch) return tag.name_ch;
|
||||
return tag.name_es || tag.name_en || tag.alias || tag.ref || tag.mrf.slice(0, 8);
|
||||
}
|
||||
|
||||
// Create a map of mrf -> display name for resolving groups
|
||||
export function createNameMap(tags: Tag[], lang: LangType): Map<string, string> {
|
||||
const map = new Map<string, string>();
|
||||
tags.forEach(tag => {
|
||||
map.set(tag.mrf, getName(tag, lang));
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
// Resolve a group mrf to its display name
|
||||
export function resolveGroupName(mrf: string | undefined, nameMap: Map<string, string>): string {
|
||||
if (!mrf) return 'Sin grupo';
|
||||
return nameMap.get(mrf) || mrf.slice(0, 8);
|
||||
}
|
||||
|
||||
const ATC_BASE = 'https://atc.tzzrdeck.me';
|
||||
|
||||
function resolveImgUrl(url: string | undefined): string {
|
||||
if (!url) return '';
|
||||
// Relative paths (e.g., "thumbs/xxx.png") need ATC base
|
||||
if (url && !url.startsWith('http')) {
|
||||
return `${ATC_BASE}/${url}`;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
export function getImg(tag: Tag): string {
|
||||
return resolveImgUrl(tag.img_thumb_url);
|
||||
}
|
||||
|
||||
export function getFullImg(tag: Tag): string {
|
||||
return resolveImgUrl(tag.img_url) || resolveImgUrl(tag.img_thumb_url);
|
||||
}
|
||||
5
deck-frontend/backups/20260113_212146/src/utils/index.ts
Normal file
5
deck-frontend/backups/20260113_212146/src/utils/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { $, $$, createElement, delegateEvent } from './dom.ts';
|
||||
export { getName, getImg, getFullImg, createNameMap, resolveGroupName } from './i18n.ts';
|
||||
export { filterTags, type FilterOptions } from './filters.ts';
|
||||
export { copyToClipboard, copyMrf } from './clipboard.ts';
|
||||
export { toast } from './toast.ts';
|
||||
21
deck-frontend/backups/20260113_212146/src/utils/toast.ts
Normal file
21
deck-frontend/backups/20260113_212146/src/utils/toast.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
let toastEl: HTMLElement | null = null;
|
||||
let toastTimeout: number | null = null;
|
||||
|
||||
export function toast(message: string, duration = 2000): void {
|
||||
if (!toastEl) {
|
||||
toastEl = document.createElement('div');
|
||||
toastEl.className = 'toast';
|
||||
document.body.appendChild(toastEl);
|
||||
}
|
||||
|
||||
if (toastTimeout) {
|
||||
clearTimeout(toastTimeout);
|
||||
}
|
||||
|
||||
toastEl.textContent = message;
|
||||
toastEl.classList.add('show');
|
||||
|
||||
toastTimeout = window.setTimeout(() => {
|
||||
toastEl?.classList.remove('show');
|
||||
}, duration);
|
||||
}
|
||||
Reference in New Issue
Block a user