|
|
@ -0,0 +1,359 @@ |
|
|
|
<script setup lang="ts"> |
|
|
|
import { ref, onMounted, h } from 'vue'; |
|
|
|
import { useI18n } from 'vue-i18n'; |
|
|
|
|
|
|
|
const { t } = useI18n(); |
|
|
|
|
|
|
|
// Resource categories |
|
|
|
const categories = ref([ |
|
|
|
{ id: 'all', name: t('resources.categories.all') }, |
|
|
|
{ id: 'docs', name: t('resources.categories.docs') }, |
|
|
|
{ id: 'tutorials', name: t('resources.categories.tutorials') }, |
|
|
|
{ id: 'sdks', name: t('resources.categories.sdks') }, |
|
|
|
{ id: 'tools', name: t('resources.categories.tools') }, |
|
|
|
{ id: 'community', name: t('resources.categories.community') } |
|
|
|
]); |
|
|
|
|
|
|
|
// Active category |
|
|
|
const activeCategory = ref('all'); |
|
|
|
|
|
|
|
// Resources list |
|
|
|
const resources = ref([ |
|
|
|
{ |
|
|
|
id: 1, |
|
|
|
title: t('resources.items.docs_title'), |
|
|
|
description: t('resources.items.docs_description'), |
|
|
|
link: 'https://docs.mose.io', |
|
|
|
category: 'docs', |
|
|
|
icon: 'document' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 2, |
|
|
|
title: t('resources.items.api_title'), |
|
|
|
description: t('resources.items.api_description'), |
|
|
|
link: 'https://api.mose.io', |
|
|
|
category: 'docs', |
|
|
|
icon: 'code' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 3, |
|
|
|
title: t('resources.items.tutorial_beginner_title'), |
|
|
|
description: t('resources.items.tutorial_beginner_description'), |
|
|
|
link: 'https://learn.mose.io/beginner', |
|
|
|
category: 'tutorials', |
|
|
|
icon: 'academic-cap' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 4, |
|
|
|
title: t('resources.items.tutorial_advanced_title'), |
|
|
|
description: t('resources.items.tutorial_advanced_description'), |
|
|
|
link: 'https://learn.mose.io/advanced', |
|
|
|
category: 'tutorials', |
|
|
|
icon: 'academic-cap' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 5, |
|
|
|
title: t('resources.items.sdk_js_title'), |
|
|
|
description: t('resources.items.sdk_js_description'), |
|
|
|
link: 'https://github.com/mose/js-sdk', |
|
|
|
category: 'sdks', |
|
|
|
icon: 'code-bracket' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 6, |
|
|
|
title: t('resources.items.sdk_python_title'), |
|
|
|
description: t('resources.items.sdk_python_description'), |
|
|
|
link: 'https://github.com/mose/python-sdk', |
|
|
|
category: 'sdks', |
|
|
|
icon: 'code-bracket' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 7, |
|
|
|
title: t('resources.items.explorer_title'), |
|
|
|
description: t('resources.items.explorer_description'), |
|
|
|
link: 'https://explorer.mose.io', |
|
|
|
category: 'tools', |
|
|
|
icon: 'magnifying-glass' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 8, |
|
|
|
title: t('resources.items.wallet_title'), |
|
|
|
description: t('resources.items.wallet_description'), |
|
|
|
link: 'https://wallet.mose.io', |
|
|
|
category: 'tools', |
|
|
|
icon: 'wallet' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 9, |
|
|
|
title: t('resources.items.forum_title'), |
|
|
|
description: t('resources.items.forum_description'), |
|
|
|
link: 'https://forum.mose.io', |
|
|
|
category: 'community', |
|
|
|
icon: 'chat-bubble-left-right' |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 10, |
|
|
|
title: t('resources.items.github_title'), |
|
|
|
description: t('resources.items.github_description'), |
|
|
|
link: 'https://github.com/mose', |
|
|
|
category: 'community', |
|
|
|
icon: 'code-bracket' |
|
|
|
} |
|
|
|
]); |
|
|
|
|
|
|
|
// Filtered resources based on active category |
|
|
|
const filteredResources = ref([]); |
|
|
|
|
|
|
|
// Filter resources based on active category |
|
|
|
const filterResources = () => { |
|
|
|
if (activeCategory.value === 'all') { |
|
|
|
filteredResources.value = resources.value; |
|
|
|
} else { |
|
|
|
filteredResources.value = resources.value.filter(resource => resource.category === activeCategory.value); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Set active category |
|
|
|
const setActiveCategory = (category: string) => { |
|
|
|
activeCategory.value = category; |
|
|
|
filterResources(); |
|
|
|
}; |
|
|
|
|
|
|
|
// Initialize with all resources |
|
|
|
onMounted(() => { |
|
|
|
filterResources(); |
|
|
|
}); |
|
|
|
|
|
|
|
// Icon components as Vue render functions |
|
|
|
const DocumentIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const CodeIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'M14.25 9.75 16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0 0 20.25 18V6A2.25 2.25 0 0 0 18 3.75H6A2.25 2.25 0 0 0 3.75 6v12A2.25 2.25 0 0 0 6 20.25Z' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const AcademicCapIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const CodeBracketIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const MagnifyingGlassIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'm21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const WalletIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'M21 12a2.25 2.25 0 0 0-2.25-2.25H15a3 3 0 1 1-6 0H5.25A2.25 2.25 0 0 0 3 12m18 0v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6m18 0V9M3 12V9m18 0a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 9m18 0V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v3' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const ChatBubbleLeftRightIcon = { |
|
|
|
render() { |
|
|
|
return h('svg', { |
|
|
|
xmlns: 'http://www.w3.org/2000/svg', |
|
|
|
fill: 'none', |
|
|
|
viewBox: '0 0 24 24', |
|
|
|
'stroke-width': '1.5', |
|
|
|
stroke: 'currentColor', |
|
|
|
class: 'w-6 h-6' |
|
|
|
}, [ |
|
|
|
h('path', { |
|
|
|
'stroke-linecap': 'round', |
|
|
|
'stroke-linejoin': 'round', |
|
|
|
d: 'M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155' |
|
|
|
}) |
|
|
|
]); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// Icon component mapping |
|
|
|
const getIconComponent = (iconName: string) => { |
|
|
|
switch (iconName) { |
|
|
|
case 'document': |
|
|
|
return DocumentIcon; |
|
|
|
case 'code': |
|
|
|
return CodeIcon; |
|
|
|
case 'academic-cap': |
|
|
|
return AcademicCapIcon; |
|
|
|
case 'code-bracket': |
|
|
|
return CodeBracketIcon; |
|
|
|
case 'magnifying-glass': |
|
|
|
return MagnifyingGlassIcon; |
|
|
|
case 'wallet': |
|
|
|
return WalletIcon; |
|
|
|
case 'chat-bubble-left-right': |
|
|
|
return ChatBubbleLeftRightIcon; |
|
|
|
default: |
|
|
|
return DocumentIcon; |
|
|
|
} |
|
|
|
}; |
|
|
|
</script> |
|
|
|
|
|
|
|
<template> |
|
|
|
<div class="py-12 px-6 md:px-12 lg:px-24"> |
|
|
|
<!-- Hero Section --> |
|
|
|
<div class="text-center mb-12 animate__animated animate__fadeIn"> |
|
|
|
<h1 class="text-4xl md:text-5xl font-bold mb-4 text-text">{{ t('resources.hero.title') }}</h1> |
|
|
|
<p class="text-xl text-text-secondary max-w-3xl mx-auto">{{ t('resources.hero.subtitle') }}</p> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Categories Filter --> |
|
|
|
<div class="flex flex-wrap justify-center gap-4 mb-12 animate__animated animate__fadeInUp animate__delay-xs"> |
|
|
|
<button |
|
|
|
v-for="category in categories" |
|
|
|
:key="category.id" |
|
|
|
@click="setActiveCategory(category.id)" |
|
|
|
class="px-4 py-2 rounded-full transition-all duration-300 hover:scale-105" |
|
|
|
:class="activeCategory === category.id |
|
|
|
? 'bg-primary text-background font-medium shadow-lg' |
|
|
|
: 'bg-background-light text-text-secondary hover:bg-background hover:text-text'" |
|
|
|
> |
|
|
|
{{ category.name }} |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Resources Grid --> |
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
|
|
|
<div |
|
|
|
v-for="resource in filteredResources" |
|
|
|
:key="resource.id" |
|
|
|
class="bg-background-light rounded-lg p-6 shadow-md transition-all duration-300 hover:shadow-xl hover:translate-y-[-4px] animate__animated animate__fadeIn" |
|
|
|
> |
|
|
|
<div class="flex items-start mb-4"> |
|
|
|
<div class="p-3 rounded-full bg-primary/10 text-primary mr-4"> |
|
|
|
<component :is="getIconComponent(resource.icon)" /> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<h3 class="text-xl font-semibold text-text mb-2">{{ resource.title }}</h3> |
|
|
|
<p class="text-text-secondary mb-4">{{ resource.description }}</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<a |
|
|
|
:href="resource.link" |
|
|
|
target="_blank" |
|
|
|
rel="noopener noreferrer" |
|
|
|
class="inline-flex items-center text-primary hover:text-primary-light transition-colors" |
|
|
|
> |
|
|
|
{{ t('resources.explore') }} |
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3" /> |
|
|
|
</svg> |
|
|
|
</a> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- Newsletter Section --> |
|
|
|
<div class="mt-16 bg-background-dark rounded-xl p-8 md:p-12 animate__animated animate__fadeIn"> |
|
|
|
<div class="max-w-4xl mx-auto"> |
|
|
|
<h2 class="text-2xl md:text-3xl font-bold mb-4 text-text">{{ t('resources.newsletter.title') }}</h2> |
|
|
|
<p class="text-text-secondary mb-6">{{ t('resources.newsletter.subtitle') }}</p> |
|
|
|
|
|
|
|
<form class="flex flex-col md:flex-row gap-4"> |
|
|
|
<input |
|
|
|
type="email" |
|
|
|
:placeholder="t('resources.newsletter.placeholder')" |
|
|
|
class="flex-grow px-4 py-3 rounded-lg bg-background border border-background-light focus:outline-none focus:ring-2 focus:ring-primary" |
|
|
|
/> |
|
|
|
<button |
|
|
|
type="submit" |
|
|
|
class="px-6 py-3 bg-primary text-background font-medium rounded-lg hover:bg-primary-light transition-colors duration-300" |
|
|
|
> |
|
|
|
{{ t('resources.newsletter.button') }} |
|
|
|
</button> |
|
|
|
</form> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |