This commit is contained in:
arussac
2026-05-31 15:35:59 +02:00
parent 48a99514b2
commit d88b71b2c6
11 changed files with 227 additions and 31 deletions

View File

@@ -48,11 +48,11 @@
<input v-model="url" class="opt-input" type="text" placeholder="URL de destination (optionnel)" />
</div>
<!-- Options : Pet (grille + position) -->
<!-- Options : Pet (grille des designs non encore possédés) -->
<div v-if="product.kind === 'pet'" class="opts">
<div class="pet-grid">
<button
v-for="d in designs"
v-for="d in availableDesigns"
:key="d.id"
class="pet-cell"
:class="{ active: petDesign === d.id }"
@@ -60,12 +60,11 @@
type="button"
>{{ d.char }}</button>
</div>
<div class="pet-pos">
<label v-for="pos in positions" :key="pos" class="opt-radio opt-radio--sm" :class="{ active: petPosition === pos }">
<input type="radio" :value="pos" v-model="petPosition" />
<span>{{ posLabel(pos) }}</span>
</label>
</div>
</div>
<!-- Preview : Skin de bouton -->
<div v-if="product.kind === 'send-skin'" class="send-skin-preview">
<div class="skin-btn-demo">{{ meta.char }}</div>
</div>
<!-- Stock limité -->
@@ -107,13 +106,14 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { computed, ref, watch } from 'vue';
import type { Product, PurchaseOptions } from '@/composables/useShop';
const props = defineProps<{
product: Product;
buying: boolean;
owns: (kind: string) => boolean;
ownedPetChars: string[];
petCount: number;
freeMode: boolean;
}>();
@@ -141,11 +141,19 @@ const url = ref('');
// Pet
const designs = computed(() => meta.value.designs ?? []);
const positions = computed<string[]>(() => meta.value.positions ?? ['left', 'right', 'both']);
const petDesign = ref<string>('');
const petPosition = ref<'left' | 'right' | 'both'>('left');
const availableDesigns = computed(() =>
designs.value.filter((d: any) => !props.ownedPetChars.includes(d.char))
);
watch(availableDesigns, (ds) => {
if (ds.length > 0 && !ds.find((d: any) => d.id === petDesign.value)) {
petDesign.value = (ds[0] as any).id;
}
}, { immediate: true });
const icon = computed(() => {
if (props.product.id === 'ip-colors') return '🎨';
if (props.product.kind === 'send-skin') return meta.value.char ?? '🖱️';
switch (props.product.kind) {
case 'ad-frame': return '📣';
case 'subscription': return '🚫';
@@ -180,6 +188,7 @@ const ownedAlready = computed(() => {
if (k === 'rich') return props.owns(props.product.id);
if (k === 'unlock') return props.owns(props.product.id);
if (k === 'ad-frame') return props.owns('ad-frame');
if (k === 'send-skin') return props.owns(props.product.id);
return false;
});
@@ -201,9 +210,6 @@ const stockPct = computed(() =>
function fmt(centi: number): string {
return (centi / 100).toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function posLabel(p: string): string {
return p === 'left' ? 'Gauche' : p === 'right' ? 'Droite' : 'Les deux';
}
function onBuy(): void {
const options: PurchaseOptions = {};
@@ -214,9 +220,8 @@ function onBuy(): void {
options.url = url.value || undefined;
}
if (props.product.kind === 'pet' || props.product.id === 'bundle-cosmetic') {
const d = designs.value.find((x: any) => x.id === petDesign.value) ?? designs.value[0];
if (d) { options.petDesign = d.id; options.petChar = d.char; }
options.petPosition = petPosition.value;
const d = availableDesigns.value.find((x: any) => x.id === petDesign.value) ?? availableDesigns.value[0];
if (d) { options.petDesign = (d as any).id; options.petChar = (d as any).char; }
}
emit('buy', props.product.id, options);
}
@@ -291,6 +296,14 @@ function onBuy(): void {
.pet-cell.active { border-color: #8844aa; }
.pet-pos { display: flex; gap: 6px; }
.send-skin-preview { display: flex; justify-content: center; padding: 8px 0; }
.skin-btn-demo {
width: 52px; height: 52px; border-radius: 50%;
background: #151525; border: 1px solid #30306a;
display: flex; align-items: center; justify-content: center;
font-size: 24px;
}
.stock { display: flex; flex-direction: column; gap: 4px; }
.stock-bar { height: 6px; background: #1a1a2a; border-radius: 3px; overflow: hidden; }
.stock-fill { height: 100%; background: linear-gradient(90deg, #ffaa00, #ff4444); }