Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
097ba10
feat: add hero section to LandingPage with Polkadot integration annou…
preschian Aug 11, 2025
8046566
feat: add FeaturedNFT component to showcase trending digital art with…
preschian Aug 11, 2025
7145a79
feat: add LatestDrops component to display recent generative art coll…
preschian Aug 11, 2025
46e68cf
feat: add CuratedArtists component to showcase selected artists on th…
preschian Aug 11, 2025
eb0c0df
feat: integrate Partners section into LandingPage and create Partners…
preschian Aug 11, 2025
187ab32
style: enhance typography and layout across landing components for im…
preschian Aug 11, 2025
6dbafcf
style: update Footer component with gradient background and backgroun…
preschian Aug 11, 2025
79f8fc6
refactor: update Navbar component structure for improved layout and s…
preschian Aug 11, 2025
7622dcb
feat: update LandingPage to promote the launch of the Chaotic NFT Mar…
preschian Aug 11, 2025
aa9dbdb
refactor: replace existing components on LandingPage with lazy-loaded…
preschian Aug 11, 2025
8cac5e7
feat: implement share functionality in FeaturedNFT component to enhan…
preschian Aug 11, 2025
e0ac98f
feat: enhance DropCard component with price formatting, badge status,…
preschian Aug 11, 2025
31ff8e1
feat: update FeaturedNFT component to use new ipfsToCfImageUrl functi…
preschian Aug 11, 2025
af521c9
Merge branch 'main' of github.com:chaotic-art/app into feat--refine-l…
preschian Aug 14, 2025
40c096f
refactor: update Navbar styles for improved consistency and clarity
preschian Aug 14, 2025
e24b508
refactor: simplify Footer component styles for improved clarity
preschian Aug 14, 2025
c846546
refactor: enhance landing page styles and layout for improved aesthet…
preschian Aug 14, 2025
c3494fc
refactor: adjust styles in CollectionCard component for improved hove…
preschian Aug 14, 2025
95af67a
refactor: update styles in DropCard and DropCollectedBy components fo…
preschian Aug 14, 2025
49afb65
refactor: increase limit of latest drops and adjust filtered drops fo…
preschian Aug 14, 2025
c90da71
Merge branch 'main' of github.com:chaotic-art/app into feat--refine-l…
preschian Aug 20, 2025
b0f7760
refactor: update price calculation methods in FeaturedNFT component
preschian Aug 20, 2025
1191845
refactor: enhance lazy loading for landing page components with rootM…
preschian Aug 20, 2025
c3fc6f7
refactor: update data fetching method in CuratedArtists component and…
preschian Aug 21, 2025
2c5e694
refactor: simplify data fetching in CuratedArtists component by using…
preschian Aug 21, 2025
5ad43b9
refactor: update FeaturedNFT and LatestDrops components to use props …
preschian Aug 21, 2025
5319fc3
refactor: increase loading placeholders in LatestDrops component for …
preschian Aug 21, 2025
3a87c08
Merge branch 'main' of github.com:chaotic-art/app into feat--refine-l…
preschian Aug 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions app/components/Footer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ const footerLinks: Array<
</script>

<template>
<UContainer class="mb-8 md:mb-16">
<footer class="bg-muted rounded-[30px] overflow-hidden">
<UContainer class="px-6 md:px-[60px] py-6 md:py-[40px]">
<footer class="bg-muted py-16 lg:py-24 relative overflow-hidden">
<UContainer class="relative">
<div class="px-6 md:px-[60px] py-6 md:py-[40px]">
<!-- Main Footer Content -->
<div class="flex flex-col md:flex-row justify-between gap-8 md:gap-0">
<!-- Become Artist Section -->
Expand Down Expand Up @@ -86,8 +86,7 @@ const footerLinks: Array<
</template>
</div>
</div>
</UContainer>
<div class="bg-cover bg-center bg-no-repeat h-[80px] md:h-[112px]" style="background-image: url('/imgs/footer-bottom-bg.png');" />
</footer>
</UContainer>
</div>
</UContainer>
</footer>
</template>
65 changes: 33 additions & 32 deletions app/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,43 +71,44 @@ function handleNavClick(item: NavigationMenuItem, event?: Event) {
</script>

<template>
<UContainer>
<nav class="rounded-full border border-border my-4 md:my-[26px] p-2 flex items-center gap-2 justify-between mx-auto overflow-hidden">
<div class="flex items-center gap-2 ml-4">
<Logo class="select-none w-6 h-6 md:w-8 md:h-8" :font-controlled="false" />
<NuxtLink
to="/" class="text-lg md:text-3xl font-medium font-serif italic text-foreground"
>
Chaotic
</NuxtLink>
</div>
<header class="border-b border-border bg-background backdrop-blur supports-[backdrop-filter]:bg-background/60 sticky top-0 z-50 w-full py-2">
<UContainer>
<nav class="flex items-center gap-2 justify-between mx-auto overflow-hidden">
<div class="flex items-center gap-2">
<Logo class="select-none w-6 h-6 md:w-8 md:h-8" :font-controlled="false" />
<NuxtLink to="/" class="text-lg md:text-xl font-bold font-serif italic text-foreground">
Chaotic
</NuxtLink>
</div>

<div class="flex items-center gap-3 md:gap-6">
<div class="hidden md:flex items-center">
<UNavigationMenu
:items="navItems"
/>
<ThemeSwitcher v-if="!accountId" />
</div>

<div class="flex items-center gap-3 md:gap-6">
<div class="hidden md:flex items-center h-10">
<UNavigationMenu
:items="navItems"
<!-- Desktop Wallet -->
<div class="hidden md:block">
<NavbarWallet />
</div>

<!-- Mobile Menu Button -->
<UButton
icon="i-heroicons-bars-3"
color="neutral"
variant="ghost"
size="sm"
class="md:hidden"
@click="isBottomSheetOpen = true"
/>
<ThemeSwitcher v-if="!accountId" />
<NavbarShoppingCart />
</div>

<!-- Desktop Wallet -->
<div class="hidden md:block">
<NavbarWallet />
</div>

<!-- Mobile Menu Button -->
<UButton
icon="i-heroicons-bars-3"
color="neutral"
variant="ghost"
size="sm"
class="md:hidden"
@click="isBottomSheetOpen = true"
/>
</div>
</nav>
</UContainer>
</nav>
</UContainer>
</header>

<!-- Mobile Bottom Sheet -->
<USlideover
Expand Down
4 changes: 2 additions & 2 deletions app/components/common/card/CollectionCard.client.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ onMounted(async () => {

<template>
<article
class="group relative rounded-xl shadow-xs hover:shadow-sm border border-border overflow-hidden transition-all duration-300 hover:-translate-y-1"
class="group relative bg-background rounded-xl shadow-xs border border-border overflow-hidden transition-all duration-300 hover:-translate-y-1"
:class="{ 'animate-pulse': isLoading }"
>
<NuxtLink
Expand All @@ -79,7 +79,7 @@ onMounted(async () => {
v-if="item.image && !isLoading && !isPlaceholder"
:src="sanitizeIpfsUrl(item.image)"
:alt="`${item.name} collection banner`"
class="w-full h-full object-cover opacity-80 transition-transform duration-300 group-hover:scale-105"
class="w-full h-full object-cover opacity-80 transition-transform duration-300"
loading="lazy"
@error="imageStatus = 'placeholder'"
>
Expand Down
167 changes: 115 additions & 52 deletions app/components/drop/DropCard.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,59 @@
<script setup lang="ts">
import type { DropItem } from '@/types'
import { formatBalance } from 'dedot/utils'
import { useMintedDropsStore } from '@/stores/dropsMinted'
import { tokenToUsd } from '@/utils/calculation'
import { getDropAttributes, isTBA } from './utils'

const props = defineProps<{
drop: DropItem
showMinted?: boolean
}>()

const { currentChain } = useChain()
const formattedDrop = ref<DropItem>()
const { decimals, chainSymbol, currentChain } = useChain()

const shouldShowDrop = computed(() =>
props.showMinted || (formattedDrop.value && !formattedDrop.value.isMintedOut),
)
const isUnlimited = computed(() => formattedDrop.value?.max && formattedDrop.value.max >= Number.MAX_SAFE_INTEGER)
const usdPrice = computed(() => tokenToUsd(Number(formattedDrop.value?.price), decimals.value, chainSymbol.value))

// Format price for display
const formattedPrice = computed(() => {
if (!formattedDrop.value?.price || isTBA(formattedDrop.value.price))
return 'TBA'

if (Number(formattedDrop.value.price) === 0)
return 'Free'

return formatBalance(formattedDrop.value.price, {
decimals: chainSpec[formattedDrop.value.chain].tokenDecimals,
symbol: chainSpec[formattedDrop.value.chain].tokenSymbol,
})
})

// Format USD equivalent
const formattedPriceUSD = computed(() => {
if (!formattedDrop.value?.price || isTBA(formattedDrop.value.price) || Number(formattedDrop.value.price) === 0)
return ''

return tokenToUsd(Number(formattedDrop.value.price), chainSpec[formattedDrop.value.chain].tokenDecimals, chainSpec[formattedDrop.value.chain].tokenSymbol)
})

// Badge text based on drop status
const badgeText = computed(() => {
if (!formattedDrop.value)
return ''

const status = formattedDrop.value.status
const isMintedOut = formattedDrop.value.isMintedOut

if (status === 'minting_live')
return 'Live Now'
if (status === 'minting_ended' || isMintedOut)
return 'Minted Out'
return 'Coming Soon'
})

onBeforeMount(async () => {
formattedDrop.value = await getDropAttributes(props.drop.alias)
Expand All @@ -24,71 +62,96 @@ onBeforeMount(async () => {
useMintedDropsStore().addMintedDrop(formattedDrop.value)
}
})

const minted = computed(() => formattedDrop.value?.minted || 0)
</script>

<template>
<NuxtLink v-if="shouldShowDrop" :to="`/${currentChain}/drops/${drop.alias}`" class="relative border rounded-xl border-gray-300 overflow-hidden hover:shadow-lg transition-shadow hover-card-effect group">
<!-- Collectors on Hover -->
<div class="absolute top-3 right-3 z-10 opacity-0 group-hover:opacity-100 transition-all duration-300 transform group-hover:scale-100 scale-95">
<div v-if="formattedDrop?.minted" class="bg-secondary backdrop-blur-sm rounded-lg shadow-lg border border-white/20 p-2">
<div class="flex items-center gap-2">
<div class="flex items-center gap-1">
<UIcon name="mdi:account-group" class="text-gray-600 dark:text-gray-400 text-sm" />
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">Collected by</span>
</div>
<NuxtLink v-if="shouldShowDrop" :to="`/${currentChain}/drops/${drop.alias}`" class="relative shadow-xs border border-border rounded-xl overflow-hidden bg-background transition-all duration-300 hover:-translate-y-1 group">
<!-- Badge -->
<div class="absolute top-3 left-3 z-10">
<UBadge>
{{ badgeText }}
</UBadge>
</div>

<!-- Image Section -->
<div class="relative aspect-square">
<img :src="sanitizeIpfsUrl(drop.image)" :alt="drop.name" class="w-full h-full object-cover">

<!-- Collectors on Hover -->
<div class="absolute bottom-3 right-3 z-10 opacity-0 group-hover:opacity-100 transition-all duration-300 transform group-hover:scale-100 scale-95">
<UButton v-if="formattedDrop?.minted" icon="mdi:account-group" size="sm">
<span>Collected by</span>
<DropCollectedBy :chain="currentChain" :collection-id="drop.collection" :max-address-count="3" size="small" no-background />
</div>
</UButton>
</div>
</div>

<img :src="sanitizeIpfsUrl(drop.image)" :alt="drop.name" class="aspect-square w-full object-cover">

<div class="p-3 md:p-4">
<p class="font-bold text-base md:text-lg mb-1 md:mb-2 line-clamp-2">
{{ drop.name }}
</p>

<div class="flex items-center justify-between mt-3">
<!-- Minting Progress -->
<div class="flex flex-col gap-1">
<div class="flex items-center gap-1 text-xs text-gray-500">
<span class="font-medium">Minted</span>
</div>
<div class="flex items-center gap-1 font-mono">
<span class="text-sm font-semibold text-[var(--text-color)]">{{ formattedDrop?.minted }}</span>
<span class="text-xs text-gray-400">/</span>
<UIcon v-if="isUnlimited" name="mdi:infinity" class="text-sm text-gray-600" />
<span v-else class="text-sm text-gray-600">{{ formattedDrop?.max }}</span>
</div>
<!-- Content Section -->
<div class="p-4 md:p-5">
<!-- Title and Creator -->
<div class="mb-4">
<h3 class="font-bold text-lg md:text-xl mb-2 line-clamp-2 text-foreground">
{{ drop.name }}
</h3>

<!-- Creator Section -->
<div v-if="drop.creator" class="flex items-center gap-2 mb-3">
<span class="text-xs text-muted-foreground font-medium">Created by</span>
<UserInfo :avatar-size="20" :address="drop.creator" :transparent-background="true" class="min-w-0" />
</div>
</div>

<!-- Price Display -->
<div class="flex flex-col items-end gap-1">
<div class="text-xs text-gray-500 font-medium">
<!-- Stats Grid -->
<div class="grid grid-cols-2 gap-3 mb-4">
<!-- Price -->
<div class="bg-muted rounded-lg p-3">
<p class="text-xs text-muted-foreground mb-1 font-medium">
Price
</div>
<div class="text-right">
<div v-if="isTBA(formattedDrop?.price)" class="px-2 py-1 bg-gray-100 rounded-md">
<span class="text-xs font-medium text-gray-600">TBA</span>
</div>
<div v-else-if="Number(formattedDrop?.price)" class="flex items-baseline gap-1">
<span class="text-sm font-semibold text-[var(--text-color)]">{{ usdPrice }}</span>
<span class="text-xs text-gray-500">USD</span>
</div>
<div v-else class="px-2 py-1 bg-green-50 rounded-md">
<span class="text-xs font-medium text-green-600">Free</span>
</div>
</div>
</p>
<p class="text-lg font-bold text-neutral-900 dark:text-white">
{{ formattedPrice }}
</p>
<p v-if="formattedPriceUSD" class="text-xs text-neutral-500 dark:text-neutral-500">
{{ formattedPriceUSD }}
</p>
</div>

<!-- Supply -->
<div class="bg-muted rounded-lg p-3">
<p class="text-xs text-muted-foreground mb-1 font-medium">
Supply
</p>
<p class="text-lg font-bold text-neutral-900 dark:text-white">
<UIcon v-if="isUnlimited" name="mdi:infinity" class="text-lg text-neutral-600" />
<span v-else>{{ formattedDrop?.max || 'Unknown' }}</span>
</p>
<p class="text-xs text-neutral-500 dark:text-neutral-500">
Total Items
</p>
</div>
</div>

<!-- Creator Section -->
<div v-if="drop.creator" class="mt-3 pt-3 border-t border-gray-100">
<div class="flex items-center gap-2">
<span class="text-xs text-gray-500 font-medium">Created by</span>
<UserInfo :avatar-size="20" :address="drop.creator" :transparent-background="true" class="min-w-0" />
<!-- Minting Progress -->
<div class="mb-4">
<div class="flex justify-between text-xs text-muted-foreground mb-2">
<span>Minted: {{ minted }}</span>
<span>{{ formattedDrop?.max && !isUnlimited ? Math.round((minted / formattedDrop.max) * 100) : 0 }}%</span>
</div>
<UProgress v-model="minted" :max="formattedDrop?.max || 0" />
</div>

<!-- Action Button -->
<UButton
color="neutral"
:variant="formattedDrop?.status === 'minting_live' ? 'solid' : 'outline'"
class="w-full text-sm"
size="md"
block
>
{{ formattedDrop?.status === 'minting_live' ? 'Mint Now' : 'View Collection' }}
</UButton>
</div>
</NuxtLink>
</template>
2 changes: 1 addition & 1 deletion app/components/drop/DropCollectedBy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ watchEffect(async () => {

<template>
<div
class="rounded-full h-[62px] md:w-auto bg-gray-100 dark:bg-neutral-800 inline-flex items-center justify-between py-1.5 px-3"
class="rounded-full h-[62px] md:w-auto bg-background inline-flex items-center justify-between py-1.5 px-3"
:class="{ '!h-full !border-none !py-1 !px-2': size === 'small', 'bg-transparent': noBackground }"
>
<div class="flex items-center">
Expand Down
Loading