feat: thème WhatsApp + fix envoi rich/compact + nav shop + refactor
All checks were successful
Deploy XIP / deploy (push) Successful in 43s
All checks were successful
Deploy XIP / deploy (push) Successful in 43s
Theming - Thème global piloté par variables CSS (:root + [data-theme]) appliqué via un attribut data-theme sur la racine app. Ajout du thème "WhatsApp" (bulles + palette verte, bulle sortante #005c4b) sans nouveau composant message. - useTheme: type Theme étendu + THEME_LAYOUT (whatsapp = layout bulles). - MessageList: sélection du composant par layout avec garde de repli (fini le <component :is="undefined">). - Fix du thème "compact" cassé : nouveau MessageItemCompact.vue (variante dense). - Surfaces migrées en variables : fond app/chat, header, bouton d'envoi, bulles. Corrections - Bug envoi rich/fichier : le backend exigeait un content texte non vide même en mode HTML/CSS/JS. Validation par présence (texte OU rich OU piece jointe) ; le front n'envoie plus d'espace bidon. Plus besoin de faux texte. - Shop : suppression de "Tout voir", navigation forcee par categorie (defaut: Publicite). Refactor (lisibilite) - Parite perks backend (ip-colors, audio-alert, send-skin-*) ; /api/shop/me renvoie myPerks precalcule ; le front consomme directement (suppression de la derivation dupliquee + nettoyage d'un artefact de merge dans useMessages). - Coherence composable-singleton : myPerks lu via useMyPerks() partout. - Extraction du composer de HomePage vers ChatComposer.vue (HomePage = layout). - Helper type parseMeta<T>() pour les metaJson (moins de any). - vue-tsc --noEmit : 0 erreur. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -76,14 +76,16 @@
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useShop, type PurchaseOptions } from '@/composables/useShop';
|
||||
import { useWallet } from '@/composables/useWallet';
|
||||
import { parseMeta, type ProductMeta } from '@/composables/useMeta';
|
||||
import ProductCard from '@/components/shop/ProductCard.vue';
|
||||
import MesPersos from '@/components/shop/MesPersos.vue';
|
||||
|
||||
const { products, loading, buying, lastError, lastSuccess, fetchProducts, fetchMe, owns, petCount, ownedPetChars, purchase } = useShop();
|
||||
const { products, buying, lastError, lastSuccess, fetchProducts, fetchMe, owns, petCount, ownedPetChars, purchase } = useShop();
|
||||
const { ip, freeMode, displayBalance, fetchWallet, topUp: walletTopUp } = useWallet();
|
||||
|
||||
// Navigation forcée par catégorie : pas de « Tout voir », on entre directement
|
||||
// dans une rubrique organisée.
|
||||
const categories = [
|
||||
{ id: 'all', label: 'Tout voir' },
|
||||
{ id: 'publicite', label: 'Publicité' },
|
||||
{ id: 'abonnements', label: 'Abonnements' },
|
||||
{ id: 'cosmetiques', label: 'Cosmétiques' },
|
||||
@@ -91,20 +93,17 @@ const categories = [
|
||||
{ id: 'promotions', label: 'Promotions' },
|
||||
{ id: 'perso', label: '✨ Mes Persos' },
|
||||
];
|
||||
const activeCat = ref('all');
|
||||
const activeCat = ref('publicite');
|
||||
|
||||
const visibleProducts = computed(() => {
|
||||
const chars = ownedPetChars();
|
||||
const base = activeCat.value === 'all'
|
||||
? products.value
|
||||
: products.value.filter((p) => p.category === activeCat.value);
|
||||
return base.filter((p) => {
|
||||
if (p.kind !== 'pet') return true;
|
||||
try {
|
||||
const designs: any[] = JSON.parse((p as any).metaJson ?? '{}').designs ?? [];
|
||||
return products.value
|
||||
.filter((p) => p.category === activeCat.value)
|
||||
.filter((p) => {
|
||||
if (p.kind !== 'pet') return true;
|
||||
const designs = parseMeta<ProductMeta>(p.metaJson).designs ?? [];
|
||||
return designs.some((d) => !chars.includes(d.char));
|
||||
} catch { return true; }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function onBuy(productId: string, options: PurchaseOptions): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user