feat: initial commit

This commit is contained in:
Klein Petr 2023-12-19 18:43:42 +01:00
commit b229c2e684
72 changed files with 12915 additions and 0 deletions

3
.eslintrc Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "@antfu"
}

77
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,77 @@
name: CI
on:
push:
branches:
- main
pull_request:
branches:
- main
- dev
jobs:
codechecks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2
- uses: actions/cache@v2
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Install Dependencies
run: pnpm install
- name: Typecheck
run: pnpm run typecheck
- name: Lint
run: pnpm run lint
# build:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: pnpm/action-setup@v2
# - uses: actions/setup-node@v3
# with:
# node-version: 16.x
# cache: pnpm
# - name: Install
# run: pnpm install
# - name: Build
# run: pnpm run build
# test-e2e:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: actions/cache@v3
# with:
# path: |
# ~/.cache
# key: cypress-cache-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
# - uses: pnpm/action-setup@v2
# - uses: pnpm/action-setup@v2
# - uses: actions/setup-node@v3
# with:
# node-version: 16.x
# cache: pnpm
# - name: Install
# run: pnpm install
# - name: Cypress PNPM Patch
# run: cp pnpm-lock.yaml package-lock.json
# - name: Cypress [Hub]
# uses: cypress-io/github-action@v4
# with:
# install-command: echo
# build: pnpm hub:generate
# start: pnpm hub:preview

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist
.DS_Store

3
.npmrc Normal file
View file

@ -0,0 +1,3 @@
shamefully-hoist=true
strict-peer-dependencies=false
shell-emulator=true

12
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,12 @@
{
"recommendations": [
"antfu.iconify",
"antfu.unocss",
"antfu.vite",
"antfu.goto-alias",
"csstools.postcss",
"dbaeumer.vscode-eslint",
"vue.volar",
"lokalise.i18n-ally"
]
}

16
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
"prettier.enable": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"files.associations": {
"*.css": "postcss"
},
"editor.formatOnSave": false,
"css.validate": false,
"workbench.colorCustomizations": {
"activityBar.background": "#401963",
"titleBar.activeBackground": "#20648b",
"titleBar.activeForeground": "#FCFAFE"
}
}

35
README.md Normal file
View file

@ -0,0 +1,35 @@
# Develit.io Nuxt Starter Template
## Features
- [💚 Nuxt 3](https://nuxt.com/) - SSR, ESR, File-based routing, components auto importing, modules, etc.
- [⚡️ Vite](https://vitejs.dev/) - Instant HMR.
- 🎨 [UnoCSS](https://github.com/antfu/unocss) - The instant on-demand atomic CSS engine.
- 😃 Use icons from any icon sets ([Iconify](https://icon-sets.iconify.design/)) in Pure CSS, powered by [UnoCSS](https://github.com/antfu/unocss).
- 🔥 The `<script setup>` syntax.
- 🍍 [State Management via Pinia](https://pinia.esm.dev)
- 📥 APIs auto importing - for Composition API, VueUse and custom composables.
- 🏎 Zero-config cloud functions and deploy (Cloudflare Page preset).
- 🦾 TypeScript, of course.
### Nuxt Modules
- [VueUse](https://github.com/vueuse/vueuse) - collection of useful composition APIs.
- [ColorMode](https://github.com/nuxt-community/color-mode-module) - dark and Light mode with auto detection made easy with Nuxt.
- [UnoCSS](https://github.com/antfu/unocss) - the instant on-demand atomic CSS engine.
- [Pinia](https://pinia.esm.dev/) - intuitive, type safe, light and flexible Store for Vue.
- [DevTools](https://devtools.nuxtjs.org/) - Unleash Nuxt Developer Experience.
Vue.
## IDE
We recommend using [VS Code](https://code.visualstudio.com/) with [Volar](https://github.com/johnsoncodehk/volar) to get the best experience (You might want to disable Vetur if you have it).

31
app.vue Normal file
View file

@ -0,0 +1,31 @@
<script setup lang="ts">
useHead({
title: 'Web 3 Privacy',
})
const { fetchData } = useData()
await fetchData()
</script>
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
<style>
html {
overscroll-behavior: none;
}
body {
background-color: #000;
color: white;
}
#__nuxt {
min-height: 100vh;
height: 100%;
width: 100%;
font-family: 'Archivo';
}
</style>

17
components/Badge.vue Normal file
View file

@ -0,0 +1,17 @@
<script lang="ts" setup>
defineProps<{
text: string
inverted?: boolean
}>()
const emits = defineEmits(['selected'])
</script>
<template>
<div
border-2px
:class="[inverted ? 'border-app-black bg-app-white hover:bg-app-black text-app-black hover:text-app-white' : 'bg-app-black border-app-white hover:bg-app-white text-app-white hover:text-app-black']"
flex items-center justify-center px-24px py-16px font-700 leading-40px cursor-pointer @click="emits('selected')"
>
{{ text }}
</div>
</template>

18
components/Button.vue Normal file
View file

@ -0,0 +1,18 @@
<script setup lang="ts">
defineProps<{
border?: boolean
}>()
</script>
<template>
<div flex items-center gap-12px px-12px py-6px cursor-pointer text-app-white :class="[border ? 'border-2px border-app-white' : 'border-0']" hover:text-app-black hover:bg-app-white>
<template v-if="!!$slots.prefix">
<div text-24px>
<slot v-if="!!$slots.prefix" name="prefix" />
</div>
</template>
<div text="14px" leading-32px font-700>
<slot />
</div>
</div>
</template>

91
components/Card.vue Normal file
View file

@ -0,0 +1,91 @@
<script lang="ts" setup>
import type { ProjectShallow } from '~/types'
defineProps<{
project: ProjectShallow
}>()
const { switcher } = storeToRefs(useData())
</script>
<template>
<NuxtLink
:to="`/project/${project.id}`"
:class="switcher ? 'flex-row' : 'lg:flex-col'"
flex w-full gap-16px hover:bg="#121212" transition-all
>
<div relative max-w="96px lg:200px" w-full h="96px lg:200px" :class="switcher ? '' : 'lg:max-w-full! lg:w-full '">
<NuxtImg :src="project?.image || '/no-image-1-1.svg'" class="w-full h-full" z-10 object-cover />
<ClientOnly>
<Badge
v-if="project.percentage"
absolute bottom-0.5 lg:bottom-0 right-0.5 lg:right-0 mr-2px mb-2px
:text="`${project.percentage}%`"
class="leading-12px! text-12px! lg:text-18px! border-0!" px="4px! lg:16px!" py="4px! lg:8px!"
/>
</ClientOnly>
</div>
<div h="96px lg:200px" lg:py-24px lg:pr-24px flex flex-col justify-center lg:justify-between lg:gap-24px w-full text-white :class="switcher ? '' : 'lg:p-16px! lg:py-16px!'">
<div w-full h-fit flex flex-col gap-8px>
<div w-fit flex items-center gap-8px @click.prevent="navigateTo(project.website, { external: true, open: { target: '_blank' } })">
<h1 text="18px lg:24px app-white" font-700 line-clamp-1 hover:underline underline-offset-3>
{{ project.title1 }}
</h1>
<UnoIcon i-web-open text-16px />
</div>
<h2 text="14px app-text-grey" overflow-hidden text-ellipsis line-clamp-2 lg:line-clamp-2>
{{ project.description }}
</h2>
</div>
<div w-full flex justify-between>
<div w-full max-w-692px grid whitespace-nowrap :class="switcher ? 'grid-cols-5' : 'grid-cols-3'" gap-24px lg:grid hidden>
<ProjectInfoItem
:check-undefined="project?.github"
:link="project?.github"
title="Github" bold text-size="18px"
>
<div flex items-center gap-8px>
<UnoIcon i-web-github text-16px />
<span>{{ project?.github ? 'YES' : 'NO' }}</span>
</div>
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.readyness"
title="Readyness" text-size="18px"
>
<div flex items-center gap-12px>
<UnoIcon i-web-live text-10px :class="(project.readyness === 'Mainnet') ? 'color-#18FF2F' : (project.readyness === 'Testnet') ? 'color-#FFA800' : (project.readyness === 'Alpha') ? 'color-#FF0000' : ''" />
<span :class="(project.readyness === 'Alpha') ? 'color-#FFA800' : 'color-white'">{{ project.readyness }}</span>
</div>
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="true" title="Team" text-size="18px">
<span v-if="project.team?.length">{{ `${project.team?.length} members` }}</span>
<span v-else color="#FF0000">{{ 'Anonymous' }}</span>
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project?.docs"
:link="project?.docs"
:color="project?.docs ? '#18FF2F' : '#FF0000'"
title="Docs" bold text-size="18px"
>
{{ project?.docs ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.audits"
:link="project?.audits?.[0]?.link ?? undefined"
:color="project?.audits ? '#18FF2F' : '#FF0000'"
title="Audit" bold text-size="18px"
>
{{ project.audits ? 'YES' : 'NO' }}
</ProjectInfoItem>
</div>
<div hidden lg:flex items-center gap-16px>
<UnoIcon v-if="project.forum" i-web-forum text-28px opacity-50 hover:opacity-100 @click.prevent="navigateTo(project.forum, { external: true, open: { target: '_blank' } })" />
<UnoIcon v-if="project.explorer" i-web-explorer text-32px opacity-50 hover:opacity-100 @click.prevent="navigateTo(project.explorer, { external: true, open: { target: '_blank' } })" />
<UnoIcon v-if="project.twitter" i-web-twitter_x text-22px opacity-50 hover:opacity-100 @click.prevent="navigateTo(project.twitter, { external: true, open: { target: '_blank' } })" />
<UnoIcon v-if="project.coingecko" i-web-coingecko text-24px opacity-50 hover:opacity-100 @click.prevent="navigateTo(project.coingecko, { external: true, open: { target: '_blank' } })" />
<UnoIcon v-if="project.newsletter" i-web-news text-28px opacity-50 hover:opacity-100 @click.prevent="navigateTo(project.newsletter, { external: true, open: { target: '_blank' } })" />
</div>
</div>
</div>
</NuxtLink>
</template>

22
components/Category.vue Normal file
View file

@ -0,0 +1,22 @@
<script lang="ts" setup>
defineProps<{
title: string
count: number
selected?: boolean
}>()
</script>
<template>
<div
hover:bg-app-bg-grey
:class="[selected ? 'bg-app-white' : 'bg-transparent']"
flex items-center gap-8px py-10px px-16px text-18px font-400 leading-24px cursor-pointer
>
<h1 hover:text-app-white :class="[selected ? 'text-app-black font-700 opacity-100' : 'text-app-white opacity-50']">
{{ title }}
</h1>
<p text-app-text-grey>
({{ count }})
</p>
</div>
</template>

View file

@ -0,0 +1,59 @@
<script setup lang="ts">
import type { InputOption } from '~/types'
const props = defineProps<{
options: InputOption[]
modelValue: string
count?: number
}>()
const emits = defineEmits(['update:modelValue', 'selected'])
const selectedValue = useVModel(props, 'modelValue', emits)
const isOptionSelected = computed(() => {
return props.options.find(option => option.value === selectedValue.value)
})
function onOptionSelected(value: string) {
emits('selected', value)
}
</script>
<template>
<HeadlessListbox v-model="selectedValue" as="div">
<div class="relative mt-2 font-700 font-24px">
<HeadlessListboxButton
class="relative w-full cursor-pointer py-8px p-16px text-left border-2px text-app-white sm:text-sm sm:leading-6"
>
<span class="block truncate mr-8px">{{ isOptionSelected?.label }} <span opacity-50>({{ isOptionSelected?.count }})</span></span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<UnoIcon i-heroicons-solid-chevron-down text-app-white />
</span>
</HeadlessListboxButton>
<transition
leave-active-class="transition ease-in duration-100" leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<HeadlessListboxOptions
class="absolute z-100 max-h-60 w-full divide-y-2px border-2px border-t-0 overflow-auto bg-app-black text-app-white focus:outline-none sm:text-sm"
>
<HeadlessListboxOption
v-for="option in props.options"
:key="option.value" v-slot="{ active, selected }" as="template"
:value="option.value" @click="onOptionSelected(option.value)"
>
<li
class="w-full relative cursor-pointer select-none py-8px p-16px"
:class="[active ? 'bg-#ffffff1a' : 'text-white']"
>
<span class="block truncate" :class="[selected ? 'font-semibold' : 'font-normal']">
{{ option.label }}
<span opacity-50>({{ option.count }})</span>
</span>
</li>
</HeadlessListboxOption>
</HeadlessListboxOptions>
</transition>
</div>
</HeadlessListbox>
</template>

12
components/EditButton.vue Normal file
View file

@ -0,0 +1,12 @@
<script setup lang="ts">
defineProps<{
to: string
}>()
</script>
<template>
<NuxtLink :to="to" flex gap-12px target="_blank">
<UnoIcon i-heroicons-solid-pencil text-24px />
<slot />
</NuxtLink>
</template>

72
components/Footer.vue Normal file
View file

@ -0,0 +1,72 @@
<script setup lang="ts">
</script>
<template>
<div app-container>
<div mt-48px>
<hr border-t-2px border-white opacity-10 w-full>
</div>
<div p-24px flex items-center>
<div shrink-0 flex gap-32px justify-start>
<NuxtImg src="/logo.svg" max-w-120px sm:w-160px cursor-pointer @click="navigateTo('/')" />
</div>
<div w-full h-fit flex justify-end items-center gap-16px sm:gap-32px mt-0>
<NuxtLink to="https://twitter.com/web3privacy" target="_blank" flex items-center gap-12px>
<UnoIcon i-web-twitter_x opacity-50 text-22px />
<span hover:underline hidden lg:block text-16px>{{ '@web3privacy' }}</span>
</NuxtLink>
<NuxtLink to="https://t.me/web3privacynow" target="_blank" flex items-center gap-12px>
<UnoIcon i-web-telegram1 opacity-50 text-24px />
<span hover:underline hidden lg:block text-16px>{{ 'Telegram' }}</span>
</NuxtLink>
<NuxtLink to="https://matrix.web3privacy.info/" target="_blank" flex items-center gap-12px>
<UnoIcon i-web-matrix opacity-50 text-24px />
<span hover:underline hidden lg:block text-16px>{{ 'Matrix' }}</span>
</NuxtLink>
<NuxtLink to="https://github.com/web3privacy" target="_blank" flex items-center gap-12px>
<UnoIcon i-web-github opacity-50 text-24px />
<span hover:underline hidden lg:block text-16px>{{ 'GitHub' }}</span>
</NuxtLink>
</div>
</div>
<div text="14px sm:16px" px-24px grid grid-cols-2 sm:grid-cols-3 gap-16px mt-32px mb-16px>
<NuxtLink to="https://docs.web3privacy.info/manifesto" opacity-50 hover:underline>
{{ 'Manifesto' }}
</NuxtLink>
<NuxtLink to="https://docs.web3privacy.info/get-involved" opacity-50 hover:underline>
{{ 'How to get involved' }}
</NuxtLink>
<NuxtLink to="https://github.com/web3privacy/grants" opacity-50 hover:underline>
{{ 'Grants / Support Us' }}
</NuxtLink>
<NuxtLink to="https://beta.web3privacy.info/events" opacity-50 hover:underline>
{{ 'Events' }}
</NuxtLink>
<NuxtLink to="https://mirror.xyz/0x0f1F3DAf416B74DB3DE55Eb4D7513a80F4841073/" opacity-50 hover:underline>
{{ 'Articles' }}
</NuxtLink>
<NuxtLink to="https://docs.web3privacy.info/events/" opacity-50 hover:underline>
{{ 'Talks' }}
</NuxtLink>
<NuxtLink to="https://docs.web3privacy.info/projects/db" opacity-50 hover:underline>
{{ 'Privacy Explorer' }}
</NuxtLink>
<NuxtLink to="https://docs.web3privacy.info/projects/usecase-db" opacity-50 hover:underline>
{{ 'Privacy use-cases database' }}
</NuxtLink>
</div>
<hr block sm:hidden border-t-2px border-white opacity-10 w-full>
<div px-24px my-16px sm:mt-32px flex flex-col sm:flex-row w-full items-center justify-between gap-18px>
<a w-full opacity-50 text-14px leading-24px font-400>{{ 'All rights not reserved, Creative commons 2024 - Web3Privacy z.s.' }}</a>
<div w-full sm:w-fit sm:justify-end items-center flex flex-row gap-6px>
<span text-14px text-app-text-grey whitespace-nowrap>{{ 'Developed by' }}</span>
<div
shrink-0
bg="[url(/develit-logo-white.svg)]" duration-300 bg-no-repeat bg-center
cursor-pointer w-full max-w-104px h-32px hover:bg="[url(/develit-logo-color.svg)]"
@click="navigateTo('https://develit.io/', { external: true })"
/>
</div>
</div>
</div>
</template>

18
components/Helper.vue Normal file
View file

@ -0,0 +1,18 @@
<script lang="ts" setup>
defineProps<{
info: string
}>()
</script>
<template>
<div relative>
<button class="peer" type="button">
<UnoIcon i-heroicons-solid-information-circle class="w-20px h-20px opacity-50 hover:opacity-100 text-app-white" />
</button>
<div
class="peer-hover:visible top-20px left-20px absolute invisible z-100 inline-block px-3 py-2 text-sm font-medium text-app-black bg-app-white"
>
{{ info }}
</div>
</div>
</template>

12
components/MenuItem.vue Normal file
View file

@ -0,0 +1,12 @@
<script lang="ts" setup>
defineProps<{
title: string
selected?: boolean
}>()
</script>
<template>
<NuxtLink text-14px hover:text-app-white leading-32px uppercase cursor-pointer :class="[selected ? 'text-app-white font-700 underline underline-offset-4px hover:no-underline' : 'text-app-text-grey font-500']">
{{ title }}
</NuxtLink>
</template>

132
components/Navigation.vue Normal file
View file

@ -0,0 +1,132 @@
<script lang="ts" setup>
const route = useRoute()
const isProjectRoute = computed(() => {
return route.fullPath.includes('/project/')
})
const showMenu = ref(false)
const { showBar, showText } = storeToRefs(useNavigaiton())
const { width } = useWindowSize()
const { y } = useWindowScroll()
watch(y, (newY, oldY) => {
if (newY < oldY && newY < 10)
showText.value = true
else
showText.value = false
})
</script>
<template>
<div v-if="showMenu" w-full flex flex-col items-center>
<NuxtLink to="https://beta.web3privacy.info/" bg-black hover:bg-white w-full text-center justify-center py-14px cursor-pointer text-app-text-grey class="hover:text-black" hover:underline underline-offset-1 text-16px font-400 uppercase>
HOME
</NuxtLink>
<NuxtLink bg-black hover:bg-white w-full text-center justify-center py-14px cursor-pointer text-white class="hover:text-black" hover:underline underline-offset-1 text-16px font-400 to="/">
PROJECTS
</NuxtLink>
<NuxtLink to="https://beta.web3privacy.info/events/" bg-black hover:bg-white w-full text-center justify-center py-14px cursor-pointer text-app-text-grey class="hover:text-black" hover:underline underline-offset-1 text-16px font-400 uppercase>
EVENTS
</NuxtLink>
<NuxtLink bg-black hover:bg-white w-full justify-center to="https://mirror.xyz/0x0f1F3DAf416B74DB3DE55Eb4D7513a80F4841073/" flex items-center py-14px cursor-pointer text-app-text-grey class="hover:text-black" hover:underline underline-offset-1 text-16px font-400 uppercase>
ARTICLES
<UnoIcon ml-10px i-web-open1 text-11px />
</NuxtLink>
<NuxtLink bg-black hover:bg-white w-full justify-center to="https://docs.web3privacy.info/" flex items-center py-14px cursor-pointer text-app-text-grey class="hover:text-black" hover:underline underline-offset-1 text-16px font-400 uppercase>
DOCS
<UnoIcon ml-10px i-web-open1 text-11px />
</NuxtLink>
</div>
<div w-full sticky md:relative top-0 z-100 bg-black lg:bg-app-bg-dark_grey>
<div v-if="!isProjectRoute" relative app-container w-full h-full>
<img
absolute w-full h-167px sm:h-207px md:h-303px object-cover src="/web3privacy_eye.webp" z-101
class="object-pos-custom left--11% top-0 max-w-250px sm:max-w-335px md:max-w-588px"
:class="[(!showText && width < 768) ? 'opacity-0' : 'opacity-10', (!showText && width < 768) ? 'translate-y--128px' : 'translate-y-0px']" duration-200ms
>
</div>
<div app-container flex items-center justify-between h-80px py-6 gap-24px>
<div flex gap-16px w-full relative z-105>
<UnoIcon shrink-0 i-web-hamburger text-24px lg:hidden @click="showMenu ? showMenu = false : showMenu = true" />
<NuxtImg max-w-160px w-full src="/logo.svg" cursor-pointer @click="navigateTo('/')" />
</div>
<div hidden lg:flex items-center justify-center>
<NuxtLink to="https://beta.web3privacy.info/" py-2 px-4 cursor-pointer text-app-text-grey class="hover:text-[#c2c2c2]" hover:underline underline-offset-1 text-16px font-400 uppercase>
HOME
</NuxtLink>
<NuxtLink py-2 px-4 cursor-pointer text-white class="hover:text-[#c2c2c2]" hover:underline underline-offset-1 text-16px font-400 to="/">
DASHBOARD
</NuxtLink>
<NuxtLink to="https://beta.web3privacy.info/events/" py-2 px-4 cursor-pointer text-app-text-grey class="hover:text-[#c2c2c2]" hover:underline underline-offset-1 text-16px font-400 uppercase>
EVENTS
</NuxtLink>
<NuxtLink to="https://mirror.xyz/0x0f1F3DAf416B74DB3DE55Eb4D7513a80F4841073/" flex items-center py-2 px-4 cursor-pointer text-app-text-grey class="hover:text-[#c2c2c2]" hover:underline underline-offset-1 text-16px font-400 uppercase>
ARTICLES
<UnoIcon ml-10px i-web-open1 text-11px />
</NuxtLink>
<NuxtLink to="https://docs.web3privacy.info/" flex items-center py-2 px-4 cursor-pointer text-app-text-grey class="hover:text-[#c2c2c2]" hover:underline underline-offset-1 text-16px font-400 uppercase>
DOCS
<UnoIcon ml-10px i-web-open1 text-11px />
</NuxtLink>
</div>
<div w-full h-full flex justify-end items-start lg:grow-0 gap-3 lg:gap-6 pt-1 lg:pt-2.5>
<NuxtLink to="https://twitter.com/web3privacy">
<UnoIcon target="_blank" i-web-twitter_x text-18px sm:text-24px opacity-50 hover:opacity-100 />
</NuxtLink>
<NuxtLink to="https://t.me/web3privacynow">
<UnoIcon target="_blank" i-web-telegram1 text-20px sm:text-24px opacity-50 hover:opacity-100 />
</NuxtLink>
<NuxtLink to="https://matrix.to/#/#web3privacy:gwei.cz">
<UnoIcon target="_blank" i-web-matrix text-20px sm:text-24px opacity-50 hover:opacity-100 />
</NuxtLink>
<NuxtLink to="https://github.com/web3privacy" target="_blank">
<UnoIcon i-web-github text-20px sm:text-24px opacity-50 hover:opacity-100 />
</NuxtLink>
</div>
</div>
</div>
<div
v-if="isProjectRoute" top-96px z-99 sticky md:static
:class="[(!showBar && width < 768) ? 'translate-y--128px' : 'translate-y-0px']" duration-200ms
>
<ProjectNavigation md:mt-16px />
</div>
<div v-else bg-black lg:bg-app-bg-dark_grey>
<div
app-container flex items-center gap-32px pb-16px pt-16px sm:pb-32px
>
<div w-full flex flex-col items-center :class="[!showText && width < 768 ? 'opacity-0' : 'opacity-100']" duration-200ms relative z-101>
<img src="/explorer.webp" max-w-155px sm:max-w-342px>
<p mt-24px hidden md:block text-14px text-center font-400 leading-24px text-app-text-grey>
{{ 'There are challenges in finding crucial technical details and comparing various privacy-focused projects.' }}<br>
{{ 'To address this, we created a platform that standardizes and expands the current ' }}
<NuxtLink to="https://web3privacy.info/" target="_blank" underline text-white>
{{ 'Web3Privacy ecosystem research.' }}
</NuxtLink><br>
{{ 'Our goal is to make this valuable information accessible to the wider Web3 community.' }}
</p>
<NuxtLink
to="https://github.com/web3privacy/privacy-projects-db"
target="_blank"
mt-12px flex text-14px items-center gap-8px sm:text-16px hover:underline
>
{{ 'Learn how to contribute' }}
<UnoIcon i-heroicons-solid-arrow-right text-24px />
</NuxtLink>
</div>
</div>
</div>
</template>
<style scoped>
.object-pos-custom {
object-position: top 20% right 0%;
}
@media only screen and (min-width: 768px) {
.object-pos-custom {
object-position: top 40% right 0%;
}
}
</style>

View file

@ -0,0 +1,12 @@
<script lang="ts" setup>
</script>
<template>
<NuxtLink w-fit px-16px py-8px flex items-center gap-12px text-app-white hover:text-app-black hover:bg-app-white uppercase cursor-pointer>
<UnoIcon i-heroicons-solid-arrow-left text-24px />
<div text="16px" leading-24px font-400>
<slot />
</div>
</NuxtLink>
</template>

View file

@ -0,0 +1,30 @@
<script setup lang="ts">
import type { Project } from '~/types'
defineProps<{
project: Project
}>()
</script>
<template>
<ProjectDetailCategoryDivider title="ACTIVITY" badge-text="3/10">
<UnoIcon i-web-code_v2 text-24px />
</ProjectDetailCategoryDivider>
<ProjectDetailContainer>
<div lg:flex lg:justify-between>
<div mt-32px>
<ProjectActivityLaunch
v-if="project.history"
:network="project.history?.title"
:date="project.history?.time"
:launch-type="project.history?.description"
:to="project.history?.link"
/>
</div>
<div flex flex-col gap-24px mt-16px>
<ProjectActivityGithub :active-devs="13" last-commit="26/11/2022" :stars="45" />
<ProjectActivityTwitter :followers="13" posts="26/11/202" :retweets="1654" />
</div>
</div>
</ProjectDetailContainer>
</template>

View file

@ -0,0 +1,30 @@
<script setup lang="ts">
defineProps<{
activeDevs: number
lastCommit: string
stars: number
}>()
</script>
<template>
<div relative grid grid-cols-2 border-2px gap-y-8px gap-x-32px p-24px lg:grid-cols-3>
<h1 px-8px text-14px font-700 bg-black absolute top--10px left-16px>
{{ 'GITHUB ACTIVITY' }}
</h1>
<div flex flex-col>
<span text-14px font-400 text-app-text-grey leading-24px>{{ 'Active Developers' }}</span>
<span text-14px font-700 leading-24px>{{ activeDevs }}</span>
</div>
<div flex flex-col>
<span text-14px font-400 text-app-text-grey leading-24px>{{ 'Last commit' }}</span>
<span text-14px font-700 leading-24px>{{ lastCommit }}</span>
</div>
<div flex gap-6px lg:flex-col lg:gap-0>
<span text-14px font-400 text-app-text-grey leading-24px>{{ 'Github stars' }}</span>
<div flex items-center gap-4px>
<UnoIcon i-web-star text-16px />
<span text-14px font-400 leading-24px>{{ stars }}</span>
</div>
</div>
</div>
</template>

View file

@ -0,0 +1,37 @@
<script setup lang="ts">
defineProps<{
network?: string
date?: string
launchType?: string
to?: string
}>()
</script>
<template>
<div flex items-start gap-18px>
<svg width="30" height="153" viewBox="0 0 30 153" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5002 5.01788L12.5012 149.5" stroke="white" stroke-dasharray="1 4" />
<circle cx="13" cy="6" r="6" fill="white" />
<rect y="129" width="30" height="24" fill="url(#paint0_linear_558_22)" />
<defs>
<linearGradient id="paint0_linear_558_22" x1="15" y1="129" x2="15" y2="150.5" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0" />
<stop offset="1" />
</linearGradient>
</defs>
</svg>
<div flex flex-col gap-8px>
<div flex flex-col>
<span text-14px font-700>{{ network }}</span>
<span text-14px font-400>{{ date }}</span>
</div>
<div flex flex-col>
<span text-14px font-400>{{ 'Launch' }}</span>
<span text-14px font-400 text-app-text-grey>{{ launchType }}</span>
</div>
<NuxtLink underline underline-offset-4 :to="to">
{{ 'Learn more' }}
</NuxtLink>
</div>
</div>
</template>

View file

@ -0,0 +1,29 @@
<script setup lang="ts">
defineProps<{
followers: number
posts: string
retweets: number
}>()
</script>
<template>
<div relative grid grid-cols-2 border-2px gap-y-8px gap-x-32px p-24px lg-grid-cols-3>
<h1 px-8px text-14px font-700 bg-black absolute top--10px left-16px>
{{ 'TWITTER ACTIVITY' }}
</h1>
<div flex flex-col>
<span text-14px font-400 text-app-text-grey leading-24px>{{ 'Followers #' }}</span>
<span text-14px font-700 leading-24px>{{ followers }}</span>
</div>
<div flex flex-col>
<span text-14px font-400 text-app-text-grey leading-24px>{{ 'Posts' }}</span>
<span text-14px font-700 leading-24px>{{ posts }}</span>
</div>
<div flex gap-6px lg:flex-col lg:gap-0>
<span text-14px font-400 text-app-text-grey leading-24px>{{ 'Retweets' }}</span>
<div flex items-center gap-4px>
<span text-14px font-400 leading-24px>{{ retweets }}x</span>
</div>
</div>
</div>
</template>

View file

@ -0,0 +1,19 @@
<script setup lang="ts">
defineProps<{
title: string
badgeText: string
}>()
</script>
<template>
<div w-full flex justify-between gap-8px lg:gap-32px mt-36px>
<div flex items-center gap-8px text-16px font-700 lg:max-w-320px lg:w-full lg:justify-end>
<slot />
{{ title }}
</div>
<div w-full flex items-center>
<hr border-t-2px border-white w-full>
<!-- <Badge text-16px h-32px :text="badgeText" /> -->
</div>
</div>
</template>

View file

@ -0,0 +1,8 @@
<template>
<div lg:flex gap-32px>
<div hidden w-full max-w-320px lg:block />
<div w-full>
<slot />
</div>
</div>
</template>

View file

@ -0,0 +1,36 @@
<script lang="ts" setup>
import type { ProjectShallow } from '~/types'
const props = defineProps<{
projects: ProjectShallow[]
}>()
const { switcher } = storeToRefs(useData())
const displayCount = ref(100)
const displayedProjects = computed(() => props.projects.slice(0, displayCount.value))
function showMoreProjects() {
displayCount.value += 50
}
</script>
<template>
<div flex flex-col items-start>
<div v-if="displayedProjects.length" grid :class="switcher ? 'grid-cols-1 lg:grid-cols-1' : 'xl:grid-cols-3 lg:grid-cols-3 sm:grid-cols-2 grid-cols-1'" gap-16px text-white w-full>
<Card
v-for="project in displayedProjects"
:key="project.id" :project="project"
/>
</div>
<div v-else>
<h3>No Projects found...</h3>
</div>
<button
v-if="displayedProjects.length < projects.length"
mt-29px text="14px" leading-24px font-700 px-12px
py-4px border-2px border-app-white
@click="showMoreProjects"
>
Load more projects
</button>
</div>
</template>

View file

@ -0,0 +1,91 @@
<script lang="ts" setup>
import type { Project } from '~/types'
const props = defineProps<{
project: Project
}>()
const availableSupport = computed(() => {
const filteredKeys = ['forum', 'discord', 'twitter', 'lens', 'farcaster', 'telegram']
if (typeof props.project.links === 'object' && (props.project.links !== null || props.project.links !== undefined))
return Object.keys(props.project.links).filter(key => filteredKeys.includes(key)).length
return 0
})
const logo = props.project?.logos?.at(0)?.url
</script>
<template>
<div lg:flex lg:gap-32px>
<NuxtImg lg:max-w-320px lg:max-h-320px shrink :src="logo ?? '/no-image-1-1.svg'" class="bg-app-bg-grey object-cover max-w-full h-full vertical-align[middle] block border-0 w-full h-[300px]" />
<div grow>
<div flex flex-col justify-between gap-32px lg:flex-row lg:items-center>
<div mt-24px>
<NuxtLink :to="project.links?.web" target="_blank" flex items-center gap-12px hover:underline underline-offset-3>
<h1 text="24px sm:32px app-white" leading-32px font-700>
{{ project.name }}
</h1>
<UnoIcon i-web-openinnew text-16px />
</NuxtLink>
<h2 text="16px app-text-grey" leading-24px mt-8px>
{{ project.project_type ?? '---' }}
</h2>
</div>
<div
border-2px
class="border-app-black bg-app-white text-app-black"
flex items-center justify-center px-32px py-16px text-32px font-700 leading-40px cursor-pointer
>
{{ '63%' }}
</div>
</div>
<div grid grid-cols-2 gap-16px my-32px lg:grid-cols-4>
<ProjectInfoItem :check-undefined="project.links?.github" title="Github" bold text-size="18px">
<template #prefix>
<UnoIcon i-web-code />
</template>
{{ project.links?.github ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.project_status?.version" title="Product readyness" bold text-size="18px">
<template #prefix>
<UnoIcon i-web-cube />
</template>
{{ project.project_status?.version }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.team?.length" title="Team" bold text-size="18px">
<template #prefix>
<UnoIcon i-web-team />
</template>
{{ `${project.team?.length} members` }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.links?.docs" title="Docs" bold text-size="18px">
<template #prefix>
<UnoIcon i-web-docs text-28px />
</template>
{{ project.links?.docs ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.audits" title="Audit" bold text-size="18px">
<template #prefix>
<UnoIcon i-web-audit text-28px />
</template>
{{ project.audits ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.links" title="Available support" bold text-size="18px">
<template #prefix>
<UnoIcon i-web-support text-28px />
</template>
{{ `${availableSupport} channels` }}
</ProjectInfoItem>
</div>
<div grid grid-cols-2 gap-16px my-32px lg:grid-cols-4>
<ProjectInfoItem :check-undefined="project.blockchain_features?.network" tooltip-link="/" title="Ecosystem" bold>
{{ project.blockchain_features?.network }}
</ProjectInfoItem>
<ProjectInfoItem invisible title="Last update" bold>
17/11/2023 23:22
</ProjectInfoItem>
</div>
</div>
</div>
</template>

View file

@ -0,0 +1,99 @@
<script lang="ts" setup>
defineProps<{
header?: string
title?: string
link?: string
textSize?: string
bold?: boolean
color?: string
opacity?: number
tooltip?: string
tooltipLink?: string
checkUndefined?: any | undefined | null
}>()
const heading = ref()
const tooltipDiv = ref()
const tooltipLeftOffset = computed(() => (tooltipDiv.value?.offsetWidth / 2) - (heading.value?.offsetWidth / 2))
const mouseOverTooltip = ref(false)
const showTooltip = ref(false)
function headingMouseOut() {
setTimeout(() => mouseOverTooltip.value ? showTooltip.value = true : showTooltip.value = false, 500)
}
function tooltipMouseOver() {
mouseOverTooltip.value = true
showTooltip.value = true
}
function tooltipMouseOut() {
mouseOverTooltip.value = false
showTooltip.value = false
}
</script>
<template>
<div flex items-start relative>
<div h-full flex items-center text-32px gap-12px>
<slot name="prefix" />
<div />
</div>
<div class="peer" flex flex-col justify-center w-full>
<h3
v-if="title"
ref="heading" w-fit text="14px sm:16px app-text-grey" leading-24px
:class="[tooltip ? 'cursor-pointer underline underline-offset-5 decoration-dotted hover:decoration-solid hover:text-app-white' : '']"
@mouseover="showTooltip = true"
@mouseout="headingMouseOut()"
>
{{ title }}
</h3>
<template v-if="checkUndefined === undefined || checkUndefined === null">
<span opacity-50 leading-24px font-700 text-14px :class="`sm:text-${textSize ?? '16px'}`">{{ 'N/A' }}</span>
</template>
<template v-else>
<template v-if="link">
<div
flex gap-6px items-center justify-start hover:underline hover:text-white text-14px
:color="color ?? '#FFF'"
class="hoverEl"
:class="`sm:text-${textSize ?? '16px'} font-${bold ? '700' : '400'} opacity-${opacity}`"
leading-24px @click.prevent="navigateTo(link, { external: true, open: { target: '_blank' } })"
>
<slot />
<UnoIcon i-web-openinnew text-16px text-app-text-grey class="customHover" />
</div>
</template>
<div v-else :color="color ?? '#FFF'" text-14px :class="`sm:text-${textSize ?? '16px'} font-${bold ? '700' : '400'} opacity-${opacity}`" leading-24px>
<slot />
</div>
</template>
</div>
<div
v-if="tooltip"
ref="tooltipDiv"
:style="{ left: `-${tooltipLeftOffset}px` }"
:class="[showTooltip ? 'visible' : 'invisible']"
class="flex flex-col max-w-232px top-40px absolute z-100 inline-block px-3 py-2 text-14px font-400 text-app-black bg-app-white"
@mouseover="tooltipMouseOver()"
@mouseout="tooltipMouseOut()"
>
{{ tooltip }}
<NuxtLink
v-if="tooltipLink"
mt-12px text-14px font-400 leading-20px underline underline-offset-2
:to="tooltipLink"
>
{{ 'Learn More' }}
</NuxtLink>
<div w-16px h-16px absolute bg-white class="top--8px left-50% translate-x--50% rotate-45 z-99" />
</div>
</div>
</template>
<style scoped>
.hoverEl:hover .customHover {
color: #FFF;
}
</style>

View file

@ -0,0 +1,11 @@
<script setup lang="ts">
</script>
<template>
<ProjectDetailCategoryDivider title="MARKET" badge-text="3/10">
<UnoIcon i-web-code_v2 text-24px />
</ProjectDetailCategoryDivider>
<ProjectDetailContainer>
<div mt-32px w-full h-500px bg-dark />
</ProjectDetailContainer>
</template>

View file

@ -0,0 +1,28 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
const route = useRoute()
const githubProjectUrl = computed(() => {
return `https://github.com/web3privacy/data/blob/main/src/projects/${route.params.id}/index.yaml`
})
</script>
<template>
<div app-container w-full flex items-center justify-between md:bg-transparent px-8px>
<NavigationButton w-230px @click="$router.back()">
<span block md:hidden>{{ 'BACK' }}</span>
<span hidden whitespace-nowrap md:block>{{ 'BACK TO LIST' }}</span>
</NavigationButton>
<hr hidden md:block border-t-2px border-white w-full>
<div flex gap-16px>
<EditButton px-16px py-8px hover:bg-white hover:text-black :to="githubProjectUrl">
<span text-16px whitespace-nowrap font-400 leading-24px hidden md:block>{{ 'EDIT RESEARCH' }}</span>
<span text-16px whitespace-nowrap font-400 leading-24px block md:hidden>{{ 'EDIT' }}</span>
</EditButton>
<!-- <ShareButton>
<span text-16px font-400 hidden md:block>{{ 'SHARE' }}</span>
</ShareButton> -->
</div>
</div>
</template>

View file

@ -0,0 +1,150 @@
<script setup lang="ts">
import type { Project } from '~/types'
defineProps<{
project: Project
}>()
</script>
<template>
<ProjectDetailCategoryDivider title="OPENESS" badge-text="3/10">
<UnoIcon i-web-code_v2 text-24px />
</ProjectDetailCategoryDivider>
<ProjectDetailContainer>
<div mt-32px text="14px sm:16px" leading-24px font-400>
<h3 text-app-text-grey>
{{ 'Project Description' }}
</h3>
<span text="14px sm:16px" leading-20px>{{ project.description ?? '---' }}</span>
</div>
<div mt-24px>
<h3 text="14px sm:16px app-text-grey" leading-20px>
{{ 'Infrastructure links' }}
</h3>
<div grid grid-cols-2 gap-16px mt-8px lg:grid-cols-4>
<ProjectOpenessLink v-if="project.links?.web" :to="project.links?.web">
<template #prefix>
<UnoIcon i-web-website />
</template>
{{ 'Website' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.blog" :to="project.links.blog">
<template #prefix>
<UnoIcon i-web-website />
</template>
{{ 'Blog' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.github" :to="project.links?.github">
<template #prefix>
<UnoIcon i-web-github opacity-30 text-27px />
</template>
{{ 'Github' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.docs" :to="project.links?.docs">
<template #prefix>
<UnoIcon i-web-documents text-24px />
</template>
{{ 'Docs' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.block_explorer" :to="project.links.block_explorer">
<template #prefix>
<UnoIcon i-web-explorer opacity-30 />
</template>
{{ 'Explorer' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.snapshot" :to="project.links.snapshot">
<template #prefix>
<UnoIcon i-web-snapshot text-32px />
</template>
{{ 'Snapshot' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.token" :to="project.links.token">
<template #prefix>
<UnoIcon i-web-token text-28px />
</template>
{{ 'Token' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.coingecko" :to="project.links.coingecko">
<template #prefix>
<UnoIcon i-web-coingecko opacity-30 text-24px />
</template>
{{ 'Coingecko' }}
</ProjectOpenessLink>
</div>
</div>
<div mt-24px>
<h3 text="14px sm:16px app-text-grey" leading-20px>
{{ 'Socials' }}
</h3>
<div grid grid-cols-2 gap-16px mt-8px lg:grid-cols-4>
<ProjectOpenessLink v-if="project.links?.forum" :to="project.links.forum">
<template #prefix>
<UnoIcon i-web-forum opacity-30 text-28px />
</template>
{{ 'Forum' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.discord" :to="project.links.discord">
<template #prefix>
<UnoIcon i-web-discord text-27px />
</template>
{{ 'Discord' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.twitter" :to="project.links.twitter">
<template #prefix>
<UnoIcon i-web-twitter_x opacity-30 text-22px />
</template>
{{ 'Twitter' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.lens" :to="project.links.lens">
<template #prefix>
<UnoIcon i-web-lens text-32px />
</template>
{{ 'Lens' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.farcaster" :to="project.links.farcaster">
<template #prefix>
<UnoIcon i-web-farcaster text-26px />
</template>
{{ 'Farcaster' }}
</ProjectOpenessLink>
<ProjectOpenessLink v-if="project.links?.telegram" :to="project.links.telegram">
<template #prefix>
<UnoIcon i-web-telegram opacity-30 text-22px />
</template>
{{ 'Telegram' }}
</ProjectOpenessLink>
</div>
</div>
<div mt-24px>
<ProjectOpenessTeamMembers :members="project.team" />
</div>
<div grid grid-cols-2 items-start mt-32px gap-y-26px lg:grid-cols-4>
<ProjectInfoItem :check-undefined="project.product_launch_day" title="Product launch day">
{{ project.product_launch_day }}
</ProjectInfoItem>
<ProjectInfoItem
title="Opensource" bold
:check-undefined="project.blockchain_features?.opensource"
:color=" project.blockchain_features?.opensource ? '#A8FF18' : '#FF0000'"
>
{{ project.blockchain_features?.opensource ? 'Yes' : 'No' }}
</ProjectInfoItem>
</div>
<div mt-32px w-full>
<ProjectInfoItem
:check-undefined="project.funding"
title="Funding" bold
w-full
>
<template v-for="fund in project.funding" :key="fund.name">
<div mt-16px grid grid-cols-2 sm:grid-cols-4>
<span v-if="fund.name">{{ fund.name }}</span>
<span v-if="fund.time" font-400>{{ fund.time }}</span>
<span v-if="fund.time" font-400 text-app-text-grey>{{ fund.type }}</span>
<span v-if="fund.time" font-400>{{ fund.value }}</span>
</div>
</template>
</ProjectInfoItem>
</div>
</ProjectDetailContainer>
</template>

View file

@ -0,0 +1,18 @@
<script setup lang="ts">
defineProps<{
to: string
}>()
</script>
<template>
<NuxtLink :to="to" flex items-center gap-12px>
<div flex justify-center items-center w-32px h-32px text-32px>
<slot name="prefix" />
</div>
<div flex flex-col justify-center>
<div text="14px sm:16px app-white" font-400 leading-24px hover:underline>
<slot />
</div>
</div>
</NuxtLink>
</template>

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,48 @@
<script setup lang="ts">
const props = defineProps<{
members: {
name: string
role?: string
link?: string
}[] | undefined
}>()
</script>
<template>
<div>
<h3 text="14px sm:16px app-text-grey" leading-24px>
{{ 'Team members' }}
</h3>
<div grid grid-cols-1 gap-16px mt-12px sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4>
<template v-if="props.members?.length">
<template v-for="member in members" :key="member.name">
<div flex gap-12px>
<template v-if="member.link">
<NuxtImg :src="member.link" width="48" height="48" :alt="member.name" />
</template>
<template v-else>
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle opacity="0.2" cx="24" cy="24" r="23.5" stroke="white" />
<g opacity="0.2">
<mask id="mask0_2200_7716" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="12" y="12" width="24" height="24">
<rect x="12" y="12" width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_2200_7716)">
<path d="M24 24C22.9 24 21.9583 23.6083 21.175 22.825C20.3917 22.0417 20 21.1 20 20C20 18.9 20.3917 17.9583 21.175 17.175C21.9583 16.3917 22.9 16 24 16C25.1 16 26.0417 16.3917 26.825 17.175C27.6083 17.9583 28 18.9 28 20C28 21.1 27.6083 22.0417 26.825 22.825C26.0417 23.6083 25.1 24 24 24ZM16 32V29.2C16 28.6333 16.1458 28.1125 16.4375 27.6375C16.7292 27.1625 17.1167 26.8 17.6 26.55C18.6333 26.0333 19.6833 25.6458 20.75 25.3875C21.8167 25.1292 22.9 25 24 25C25.1 25 26.1833 25.1292 27.25 25.3875C28.3167 25.6458 29.3667 26.0333 30.4 26.55C30.8833 26.8 31.2708 27.1625 31.5625 27.6375C31.8542 28.1125 32 28.6333 32 29.2V32H16Z" fill="white" />
</g>
</g>
</svg>
</template>
<div flex flex-col>
<span text="14px sm:16px" font-700>{{ member.name }}</span>
<span text="14px sm:16px app-text-grey" font-400>{{ member.role ?? 'N/A' }}</span>
</div>
</div>
</template>
</template>
<template v-else>
<span text-14px opacity-50>{{ 'N/A' }}</span>
</template>
</div>
</div>
</template>

View file

@ -0,0 +1,129 @@
<script setup lang="ts">
import type { Project } from '~/types'
defineProps<{
project: Project
}>()
</script>
<template>
<ProjectDetailCategoryDivider title="PRIVACY" badge-text="3/10">
<UnoIcon i-web-code_v2 text-24px />
</ProjectDetailCategoryDivider>
<ProjectDetailContainer>
<div grid grid-cols-2 items-start mt-32px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.p2p"
bold title="Peer to Peer (P2P)"
>
{{ project.blockchain_features?.p2p ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.tracebility?.kyc"
bold :color="project.tracebility?.kyc ? '#FF0000' : '#18FF2F'"
title="Know Your Customer (KYC)"
>
{{ project.tracebility?.kyc ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.storage"
bold title="Decentralized storage"
:color="project.storage?.decentralized ? '#18FF2F' : '#FF0000'"
>
{{ project.storage?.decentralized ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.default_privacy"
title="Default privacy"
:color="project.default_privacy ? '#18FF2F' : '#FF0000'"
>
{{ project.default_privacy ? 'YES' : 'NO' }}
</ProjectInfoItem>
</div>
<div grid grid-cols-2 items-start mt-32px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.revealed_recipient"
:color="project.blockchain_features?.revealed_recipient ? '#FF0000' : '#18FF2F'"
bold title="Revealed recipient"
>
{{ project.blockchain_features?.revealed_recipient ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.revealed_sender"
:color="project.blockchain_features?.revealed_sender ? '#FF0000' : '#18FF2F'"
bold title="Revealed sender"
>
{{ project.blockchain_features?.revealed_sender ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.revealed_ammount"
:color="project.blockchain_features?.revealed_ammount ? '#FF0000' : '#18FF2F'"
bold title="Revealed amount"
>
{{ project.blockchain_features?.revealed_ammount ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.reversability_condition"
bold title="Reversability"
>
{{ project.blockchain_features?.reversability_condition }}
</ProjectInfoItem>
</div>
<div grid grid-cols-2 items-start mt-32px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.connected_tx"
:color="project.blockchain_features?.connected_tx ? '#FF0000' : '#18FF2F'"
bold title="Connected Txs"
>
{{ project.blockchain_features?.connected_tx ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.blockchain_features?.data_masking" bold title="Data masking">
{{ project.blockchain_features?.data_masking }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.blockchain_features?.tx_history" bold title="Tx history">
{{ project.blockchain_features?.tx_history ? 'YES' : 'NO' }}
</ProjectInfoItem>
</div>
<div my-24px>
<hr border-t-2px border-white opacity-20 w-80px>
</div>
<div grid grid-cols-2 items-start mt-32px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.privacy_policy"
:color="project.privacy_policy?.defined ? '#18FF2F' : '#FF0000'"
bold title="Privacy Policy"
>
{{ project.privacy_policy?.defined ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.tracebility?.tracked_data" title="Collected data">
{{ project.tracebility?.tracked_data }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.privacy_policy?.data_usage" title="Data usage">
{{ project.privacy_policy?.data_usage }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.blockchain_features?.frontend_anonymity" bold title="Frontend anonymity">
{{ project.blockchain_features?.frontend_anonymity }}
</ProjectInfoItem>
</div>
<div my-24px>
<hr border-t-2px border-white opacity-20 w-80px>
</div>
<div grid grid-cols-2 items-start mt-32px gap-y-16px>
<ProjectInfoItem
:check-undefined="project.compliance"
:color="project.compliance ? '#FF0000' : '#18FF2F'"
bold title="Compliance"
>
{{ project.compliance ? 'YES' : 'NO' }}
</ProjectInfoItem>
</div>
<div grid grid-cols-2 items-start mt-32px gap-y-16px>
<ProjectInfoItem :check-undefined="project.tracebility?.sign_in_type_requirments" bold title="Sign-in requirements">
{{ project.tracebility?.sign_in_type_requirments }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.blockchain_features?.identity_integration" title="Identity integrations">
{{ project.blockchain_features?.identity_integration }}
</ProjectInfoItem>
</div>
</ProjectDetailContainer>
</template>

View file

@ -0,0 +1,65 @@
<script lang="ts" setup>
import type { Project } from '~/types'
defineProps<{
project: Project
}>()
</script>
<template>
<ProjectDetailCategoryDivider title="SECURITY" badge-text="3/10">
<UnoIcon i-web-code_v2 text-24px />
</ProjectDetailCategoryDivider>
<ProjectDetailContainer>
<div grid grid-cols-2 items-start mt-32px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.asset_custody_type"
bold title="Asset custody"
>
{{ project.blockchain_features?.asset_custody_type }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.upgradability"
title="Upgradability"
>
<b :color="project.blockchain_features?.upgradability?.enabled ? '#FF0000' : '#18FF2F'">
{{ project.blockchain_features?.upgradability?.enabled ? 'YES' : 'NO' }}
</b>
{{ ` ${project.blockchain_features?.upgradability?.type}` }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.social_trust" title="Social dependency">
{{ project.social_trust }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.third_party_dependency"
bold :color="project.third_party_dependency ? '#FF0000' : '#18FF2F'" title="Third-party dependency"
>
{{ project.third_party_dependency ? 'YES' : 'NO' }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.technical_spof"
:color="project.technical_spof ? '#FF0000' : '#18FF2F'"
title="Technical dependency"
>
{{ project.technical_spof }}
</ProjectInfoItem>
</div>
<div my-24px>
<hr border-t-2px border-white opacity-20 w-80px>
</div>
<div v-if="project.audits">
<h2 text-18px text-app-text-grey my-24px>
Audits
</h2>
<template v-for="audit in project.audits" :key="audit.name">
<ProjectSecurityAudit
:audit-name="audit.name"
:audit-url="audit.link"
:date="audit.time"
>
<NuxtImg :src="audit.logo ?? '/no-image-1-1.svg'" w-64px h-64px object-cover />
</ProjectSecurityAudit>
</template>
</div>
</ProjectDetailContainer>
</template>

View file

@ -0,0 +1,20 @@
<script setup lang="ts">
defineProps<{
auditName: string
auditUrl?: string
date?: string
}>()
</script>
<template>
<div flex items-center gap-16px>
<slot />
<div flex flex-col text-14px font-700 leading-24px>
<NuxtLink hover:underline flex items-center gap-8px :to="auditUrl">
{{ auditName }}
<UnoIcon v-if="auditUrl" i-web-openinnew text-16px />
</NuxtLink>
<span text-14px font-400 leading-24px>{{ date }}</span>
</div>
</div>
</template>

View file

@ -0,0 +1,73 @@
<script setup lang="ts">
import type { Project } from '~/types'
defineProps<{
project: Project
}>()
</script>
<template>
<ProjectDetailCategoryDivider title="TECHNOLOGY" badge-text="3/10">
<UnoIcon i-web-code_v2 text-24px />
</ProjectDetailCategoryDivider>
<ProjectDetailContainer>
<div grid grid-cols-2 items-start mt-32px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.technology?.type"
title="Technology type" bold
>
{{ project.technology?.type }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.blockchain_features?.encryption"
title="Encryption"
bold
>
{{ project.blockchain_features?.encryption }}
</ProjectInfoItem>
<ProjectInfoItem
title="License"
bold
:check-undefined="project.licences"
>
{{ project.licences }}
</ProjectInfoItem>
<ProjectInfoItem :check-undefined="project.links?.whitepaper" title="Whitepaper" bold :link="project.links?.whitepaper">
{{ project.links?.whitepaper ? 'YES' : 'NO' }}
</ProjectInfoItem>
</div>
<div grid grid-cols-1 items-start mt-16px gap-y-16px lg:grid-cols-4>
<ProjectInfoItem
:check-undefined="project.project_status"
title="Version"
bold
>
<div v-if="project.project_status?.live_status" flex items-center gap-12px>
<UnoIcon i-web-live text-10px class="color-#B5E26B" />
<span>{{ `Live on ${project.project_status.version}` }}</span>
</div>
<div v-else flex items-center gap-12px>
<UnoIcon i-web-live text-10px class="color-#e26b6b" />
<span>{{ 'Offline' }}</span>
</div>
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.technology?.features"
title="Features"
bold
>
{{ project.technology?.features.join(', ') }}
</ProjectInfoItem>
<ProjectInfoItem
:check-undefined="project.client_diversability"
title="Client diversability" bold
>
<template v-for="item in project.client_diversability" :key="item.name">
<NuxtLink :to="item.link">
{{ item.name }}
</NuxtLink><br>
</template>
</ProjectInfoItem>
</div>
</ProjectDetailContainer>
</template>

52
components/SearchBox.vue Normal file
View file

@ -0,0 +1,52 @@
<script setup lang="ts">
import type { InputOption } from '~/types'
const { filter, switcher } = storeToRefs(useData())
const options: InputOption[] = [
{ label: 'A to Z', value: 'atoz' },
{ label: 'Score', value: 'score' },
{ label: 'Anonymity', value: 'anonymity' },
]
const isSearchFocused = ref(false)
</script>
<template>
<div flex justify-between gap-16px items-centeer>
<div border-2px flex items-center max-w-320px w-full hover:opacity-100 :class="isSearchFocused ? 'opacity-100' : 'opacity-25'">
<div px-12px py-0px flex w-fit>
<UnoIcon
i-web-search text-16px
:class="isSearchFocused ? 'opacity-100' : 'opacity-50' "
class="uno-icon"
/>
</div>
<input
v-model="filter.query" type="text"
bg-transparent border-transparent
focus:border-transparent focus:ring-0
h-full w-full outline-none @focus="isSearchFocused = true" @blur="isSearchFocused = false"
>
</div>
<div>
<div flex gap-24px items-center w-full>
<div gap-12px items-center xl:flex hidden invisible>
<UnoIcon i-web-list text-16px cursor-pointer hover:opacity-100 :class="switcher ? 'opacity-100' : 'opacity-50'" @click="switcher = true" />
<UnoIcon i-web-blocks text-16px cursor-pointer hover:opacity-100 :class="!switcher ? 'opacity-100' : 'opacity-50'" @click="switcher = false" />
</div>
<div flex gap-12px items-center w-full>
<h4 xl:block hidden text-16px opacity-50 w-fit>
Sort by
</h4>
<SelectBox v-model="filter.sortby" :black-and-white="false" :options="options" w-99px sm:w-162px text-black :is-margin-top="false" />
</div>
</div>
</div>
</div>
</template>
<style scoped>
div:hover .uno-icon {
opacity: 1;
}
</style>

54
components/SelectBox.vue Normal file
View file

@ -0,0 +1,54 @@
<script setup lang="ts">
import type { InputOption } from '~/types'
const props = withDefaults(defineProps<SelectProps>(), {
isMarginTop: true,
blackAndWhite: true,
})
const emits = defineEmits(['update:modelValue'])
interface SelectProps {
options: InputOption[]
modelValue: string
isMarginTop?: boolean
blackAndWhite?: boolean
}
const selectedValue = useVModel(props, 'modelValue', emits)
</script>
<template>
<HeadlessListbox v-model="selectedValue" as="div">
<div class="relative font-700" :class="[isMarginTop ? 'mt-2' : 'mt-0', blackAndWhite ? 'bg-app-black' : 'bg-app-white']">
<HeadlessListboxButton
class="relative w-full cursor-pointer py-8px p-16px text-left border-2px sm:text-sm sm:leading-6" :class="[blackAndWhite ? ' text-app-white' : 'text-app-black']"
>
<span class="block truncate mr-8px">{{ props.options.find(option => option.value === selectedValue)?.label }}</span>
<span class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
<UnoIcon i-heroicons-solid-chevron-down :class="[blackAndWhite ? ' text-app-white' : 'text-app-black']" />
</span>
</HeadlessListboxButton>
<transition
leave-active-class="transition ease-in duration-100" leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<HeadlessListboxOptions
class="absolute z-100 max-h-60 w-full divide-y-2px border-2px border-t-0 overflow-auto bg-app-black text-app-white focus:outline-none sm:text-sm"
>
<HeadlessListboxOption
v-for="option in props.options" :key="option.value" v-slot="{ active, selected }"
as="template" :value="option.value"
>
<li
class="w-full relative cursor-pointer select-none py-8px p-16px"
:class="[active ? 'bg-#ffffff1a' : 'text-white']"
>
<span class="block truncate" :class="[selected ? 'font-semibold' : 'font-normal']">{{ option.label }}</span>
</li>
</HeadlessListboxOption>
</HeadlessListboxOptions>
</transition>
</div>
</HeadlessListbox>
</template>

View file

@ -0,0 +1,9 @@
<script setup lang="ts">
</script>
<template>
<div flex gap-12px>
<UnoIcon i-heroicons-solid-share text-24px />
<slot />
</div>
</template>

20
components/Tabs.vue Normal file
View file

@ -0,0 +1,20 @@
<script lang="ts" setup>
import type { InputOption } from '~/types'
const props = defineProps<{
options: InputOption[]
modelValue: string
}>()
const emits = defineEmits(['update:modelValue'])
const selectedValue = useVModel(props, 'modelValue', emits)
</script>
<template>
<div flex items-center gap-16px>
<div v-for="option in options" :key="option.value" :class="[selectedValue === option.value ? 'font-700 text-app-black bg-app-white' : 'font-400 text-app-white border-app-white']" text-24px leading-32px px-16px py-8px border-2px cursor-pointer @click="selectedValue = option.value">
{{ option.label }}
</div>
</div>
</template>

135
composables/useData.ts Normal file
View file

@ -0,0 +1,135 @@
import type { Category, Project, ProjectShallow } from '~/types'
export const useData = defineStore('data', () => {
const categories = useState<Category[]>('categories')
const projects = useState<Project[]>('projects')
const selectedCategoryId = useState(() => 'defi')
const filter = reactive({
query: '',
sortby: 'atoz',
})
const switcher = ref(true)
watch(selectedCategoryId, () => {
if (selectedCategoryId.value !== 'all')
filter.query = ''
})
watch(filter, () => {
if (filter.query !== '')
selectedCategoryId.value = 'all'
})
const fetchData = async () => {
try {
const data = await $fetch<{
categories: Category[]
projects: Project[]
}>('/api/data')
projects.value = data.projects.filter(p => p.name)
categories.value = data.categories.map((c) => {
c.projectsCount = projects.value.filter(p =>
p.categories?.includes(c.id),
).length
return c
}).filter(c => c.projectsCount > 0)
}
catch (e) {
console.error(e)
return false
}
return true
}
const projectToShallow = (project: Project): ProjectShallow => {
const availableSupport = () => {
const filteredKeys = ['forum', 'discord', 'twitter', 'lens', 'farcaster', 'telegram']
if (typeof project.links === 'object' && (project.links !== null || project.links !== undefined))
return Object.keys(project.links).filter(key => filteredKeys.includes(key)).length
return 0
}
return {
id: project.id,
title1: project.name,
description: project.description ?? 'N/A',
percentage: Math.floor(Math.random() * 91),
forum: project.links?.forum,
explorer: project.links?.block_explorer,
twitter: project.links?.twitter,
coingecko: project.links?.coingecko,
newsletter: project.links?.rss_feed,
github: project.links?.github,
website: project.links?.web,
readyness: project.project_status?.version,
team: project.team,
docs: project.links?.docs,
audits: project.audits,
support: availableSupport(),
image: project.logos?.[0]?.url ?? '',
anonymity: true,
}
}
const shallowProjects = computed(() => projects.value.map(project => projectToShallow(project)))
const getProjectsByCategory = <T extends ProjectShallow>(id: string, options?: { shallow: boolean }): T[] => {
if (id === 'all')
return projects.value.map(project => projectToShallow(project)) as T[]
else
return projects.value.filter(project => project.categories?.includes(id)).map(project => options?.shallow ? projectToShallow(project) : project) as T[]
}
const getProjectById = <T extends Project | ProjectShallow>(id: string, options?: { shallow: boolean }): T => {
const project = projects.value.find(project => project.id === id)
return (options?.shallow && project ? projectToShallow(project) : project) as T
}
const filteredProjects = computed(() => {
if (!projects.value)
return []
const query = filter.query.toLowerCase()
const filteredShallowProjects = getProjectsByCategory(selectedCategoryId.value, { shallow: true })
.filter((project) => {
return (
project
&& project.title1
&& project.title1.toLowerCase().includes(query)
)
}).filter((project) => {
if (filter.sortby === 'anonymity')
return project.anonymity === true
else
return true
}).sort((a, b) => {
if (filter.sortby === 'score')
return b.percentage - a.percentage
if (filter.sortby === 'atoz')
return a.title1.localeCompare(b.title1)
else
return 0
})
return filteredShallowProjects
})
const filteredProjectsCount = computed(() => filteredProjects.value.length)
return {
selectedCategoryId,
filter,
switcher,
categories,
projects,
shallowProjects,
filteredProjectsCount,
fetchData,
getProjectById,
getProjectsByCategory,
filteredProjects,
projectToShallow,
}
})

View file

@ -0,0 +1,9 @@
export const useNavigaiton = defineStore('navigation', () => {
const showBar = ref(true)
const showText = ref(true)
return {
showBar,
showText,
}
})

92
layouts/default.vue Normal file
View file

@ -0,0 +1,92 @@
<script lang="ts" setup>
import type { InputOption } from '~/types'
const { categories, filteredProjectsCount, selectedCategoryId } = storeToRefs(useData())
const categoriesOptions = ref(categories.value ? categories.value.map(c => ({ label: c.name, value: c.id, count: c.projectsCount })) : [])
const extendedOptions: InputOption[] = [
...categoriesOptions.value,
]
const selectedCategory = computed(() => {
return categories.value.find(c => c.id === selectedCategoryId.value)
})
const sortedFilteredCategories = computed(() => ([
categories.value.find(c => c.id === 'defi')!,
...categories.value.sort((a, b) => a.name.localeCompare(b.name)).filter(c => c.id !== 'defi'),
]))
const { showBar } = storeToRefs(useNavigaiton())
const swipeEl = ref()
const { y } = useWindowScroll()
const scrollEl = ref()
const { y: scrollY } = useScroll(scrollEl)
const { top } = useElementBounding(scrollEl)
watch(y, (newY, oldY) => {
if (newY > oldY && newY > 32)
showBar.value = false
else
showBar.value = true
})
watch([scrollY, top, y], (newValues, oldValues) => {
if (top.value <= 32)
scrollY.value += newValues[2] - oldValues[2]
})
</script>
<template>
<div ref="swipeEl" flex flex-col min-h-100vh h-full w-full>
<Navigation />
<div app-container mt-32px>
<div flex w-full xl:gap-32px>
<div w-fit>
<div
ref="scrollEl"
class="no-scrollbar"
h-100vh overflow-y-auto sticky top-32px hidden xl:block min-w-234px pb-48px
>
<Category
v-for="category in sortedFilteredCategories"
:key="category.id" :title="category.name"
:count="category.projectsCount" :selected="selectedCategoryId === category.id"
@click="[navigateTo(`/category/${category.id}`), selectedCategoryId = category.id]"
/>
</div>
</div>
<div w-full>
<div flex flex-col gap-16px w-full>
<div xl:hidden block>
<h2 text-14px font-700>
Choose category
</h2>
<CategorySelectBox v-model="selectedCategoryId" :options="extendedOptions" w-full @selected="selectedCategoryId === 'all' ? navigateTo(`/`) : navigateTo(`/category/${selectedCategoryId}`)" />
</div>
<SearchBox />
</div>
<div flex gap-28px items-center my-24px mt-28px>
<h2 v-if="selectedCategoryId" w-max font-700 text-18px sm:text-28px whitespace-nowrap>
{{ selectedCategoryId === 'all' ? `${filteredProjectsCount} All Projects` : `${filteredProjectsCount ?? 0} ${selectedCategory?.name}` }}
</h2>
<div h-2px w="full" bg-white />
</div>
<slot />
</div>
</div>
</div>
<Footer justify-self-end />
</div>
</template>
<style scoped>
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
</style>

19
layouts/detail.vue Normal file
View file

@ -0,0 +1,19 @@
<script lang="ts" setup>
const { showBar } = storeToRefs(useNavigaiton())
const swipeEl = ref()
const { y } = useWindowScroll()
watch(y, (newY, oldY) => {
if (newY > oldY && newY > 32)
showBar.value = false
else
showBar.value = true
})
</script>
<template>
<div ref="swipeEl" h-full w-full>
<Navigation />
<slot />
</div>
</template>

51
nuxt.config.ts Normal file
View file

@ -0,0 +1,51 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
modules: [
'@vueuse/nuxt',
'@unocss/nuxt',
'@pinia/nuxt',
'@nuxtjs/color-mode',
'@nuxt/devtools',
'nuxt-lodash',
'nuxt-headlessui',
'@nuxt/image',
],
sourcemap: {
server: true,
client: false,
},
pinia: {
storesDirs: ['~/composables'],
},
experimental: {
// when using generate, payload js assets included in sw precache manifest
// but missing on offline, disabling extraction it until fixed
payloadExtraction: false,
inlineSSRStyles: false,
reactivityTransform: true,
// typedPages: true,
componentIslands: true,
},
css: [
'@unocss/reset/tailwind.css',
],
colorMode: {
classSuffix: '',
},
nitro: {
preset: 'vercel',
esbuild: {
options: {
target: 'esnext',
},
},
},
vite: {
build: {
target: 'esnext',
},
},
lodash: {
prefix: '_',
},
})

44
package.json Normal file
View file

@ -0,0 +1,44 @@
{
"name": "develitesse-nuxt",
"private": true,
"packageManager": "pnpm@8.12.1",
"scripts": {
"build": "nuxt build",
"cleanup": "nuxt cleanup",
"dev": "nuxt dev",
"generate": "nuxt generate",
"postinstall": "nuxt prepare",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"preview": "nuxt preview",
"taze:up": "taze major -I",
"test": "vitest",
"typecheck": "nuxt typecheck --no-emit"
},
"devDependencies": {
"@antfu/eslint-config": "0.43.1",
"@formkit/auto-animate": "^0.8.1",
"@iconify-json/heroicons-outline": "^1.1.10",
"@iconify-json/heroicons-solid": "^1.1.11",
"@nuxt/devtools": "^1.0.6",
"@nuxt/image": "^1.1.0",
"@nuxt/test-utils": "^3.9.0",
"@nuxtjs/color-mode": "^3.3.2",
"@pinia/nuxt": "^0.5.1",
"@unocss/nuxt": "^0.58.0",
"@vueuse/nuxt": "^10.7.0",
"eslint": "^8.56.0",
"nuxt": "^3.8.2",
"nuxt-headlessui": "^1.1.4",
"nuxt-lodash": "^2.5.3",
"pinia": "^2.1.7",
"pnpm": "^8.12.1",
"taze": "^0.13.0",
"typescript": "^5.3.3",
"vitest": "^1.1.0",
"vue-tsc": "^1.8.25"
},
"overrides": {
"vue": "latest"
}
}

15
pages/category/[id].vue Normal file
View file

@ -0,0 +1,15 @@
<script lang="ts" setup>
const dataStore = useData()
const { selectedCategoryId, filteredProjects } = storeToRefs(dataStore)
const route = useRoute()
onMounted(() => {
if (route.params.id)
selectedCategoryId.value = route.params.id as string
})
</script>
<template>
<ProjectGrid :projects="filteredProjects" />
</template>

7
pages/index.vue Normal file
View file

@ -0,0 +1,7 @@
<script lang="ts" setup>
const { filteredProjects } = storeToRefs(useData())
</script>
<template>
<ProjectGrid :projects="filteredProjects" />
</template>

44
pages/project/[id].vue Normal file
View file

@ -0,0 +1,44 @@
<script lang="ts" setup>
import type { Project } from '~/types'
definePageMeta({
layout: 'detail',
})
const route = useRoute()
const dataStore = useData()
const { getProjectById } = dataStore
const { projects } = storeToRefs(dataStore)
const project = ref<Project>()
await until(projects).toMatch(p => p?.length > 0)
project.value = getProjectById<Project>(route.params.id as string)
if (!project.value) {
throw createError({
statusCode: 404,
message: 'Project not found',
fatal: true,
})
}
</script>
<template>
<div v-if="project">
<div app-container>
<div flex flex-col gap-48px mt-54px>
<div>
<div>
<ProjectHeading :project="project" />
<ProjectOpeness :project="project" />
<ProjectTechnology :project="project" />
<ProjectPrivacy :project="project" />
<ProjectSecurity :project="project" />
<!-- <ProjectActivity :project="project" />
<ProjectMarket /> -->
</div>
</div>
</div>
</div>
<Footer />
</div>
</template>

59
pages/styleguide.vue Normal file
View file

@ -0,0 +1,59 @@
<script lang="ts" setup>
import type { InputOption } from '~/types'
const options: InputOption[] = [
{ label: 'All', value: 'all' }, { label: 'Zk-tech', value: 'zk' },
]
const loremIpsum: string = 'Description just text Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi tempus nulla quis enim vulputate semper. Duis varius nibh sed magna tincidunt pellentesque.'
const cardData = {
image: 'https://ctrlv.cz/Epud',
title1: 'title1',
title2: 'title2',
percentage: 84,
anonymity: true,
decentralized: true,
opensource: true,
license: 'MIT',
datatracking: true,
compliance: true,
}
const selectedOption = ref('all')
</script>
<template>
<div flex flex-col items-start justify-start gap-8px>
<Category title="All Projects" :count="503" />
<Category title="All Projects" :count="503" selected />
<Button mt-16px>
<template #prefix>
<UnoIcon i-heroicons-solid-pencil />
</template>
Edit Research
</Button>
<Button>
Save Research
</Button>
<MenuItem title="Dashboard" mt-16px />
<MenuItem title="Dashboard" selected />
<Badge text="84%" />
<Badge text="84%" inverted />
<SelectBox v-model="selectedOption" mt-16px :options="options" />
<h1 custom-link mt-16px>
Custom Link
</h1>
<Helper :info="loremIpsum" />
<NavigationButton @click="$router.back()">
Back
</NavigationButton>
<Tabs v-model="selectedOption" mt-16px :options="options" />
<!-- <Card
v-if="cardData"
id="test"
:image="cardData.image" :title1="cardData.title1" :title2="cardData.title2" :percentage="cardData.percentage"
:opensource="cardData.opensource" :license="cardData.license"
/> -->
</div>
</template>

10382
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

BIN
public/explorer.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

17
public/logo.svg Normal file
View file

@ -0,0 +1,17 @@
<svg width="177" height="48" viewBox="0 0 177 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M45.1797 45.1384H47.269V36.6905H45.1797V34.1016H51.719V35.9002C52.135 35.2098 52.6596 34.6921 53.2927 34.3469C53.944 34.0017 54.7037 33.8291 55.572 33.8291C56.6212 33.8291 57.5256 34.0744 58.2854 34.5649C59.0451 35.0372 59.633 35.7276 60.0491 36.636C60.4651 37.5444 60.6732 38.6072 60.6732 39.8244V45.1384H62.464V47.7273H54.2696V45.1384H55.9519V40.1787C55.9519 39.0159 55.7891 38.1439 55.4635 37.5625C55.1378 36.9812 54.6856 36.6905 54.1068 36.6905C53.6726 36.6905 53.2927 36.854 52.9671 37.181C52.6415 37.4899 52.3883 37.935 52.2074 38.5163C52.0446 39.0977 51.9632 39.7699 51.9632 40.5329V45.1384H53.5912V47.7273H45.1797V45.1384Z" fill="white"/>
<path d="M71.153 47.9998C69.6335 47.9998 68.2859 47.7 67.1101 47.1005C65.9524 46.501 65.0479 45.6653 64.3967 44.5934C63.7455 43.5215 63.4198 42.2952 63.4198 40.9144C63.4198 39.5337 63.7455 38.3074 64.3967 37.2355C65.0479 36.1636 65.9614 35.3279 67.1372 34.7284C68.313 34.1289 69.6516 33.8291 71.153 33.8291C72.6544 33.8291 73.993 34.1289 75.1688 34.7284C76.3447 35.3279 77.2582 36.1636 77.9094 37.2355C78.5606 38.3074 78.8862 39.5337 78.8862 40.9144C78.8862 42.277 78.5515 43.4942 77.8822 44.5661C77.231 45.638 76.3175 46.4828 75.1417 47.1005C73.9659 47.7 72.6363 47.9998 71.153 47.9998ZM71.153 45.4654C71.7138 45.4654 72.1932 45.3019 72.5911 44.9749C72.9891 44.6297 73.2876 44.121 73.4865 43.4488C73.6855 42.7766 73.785 41.9318 73.785 40.9144C73.785 39.8971 73.6855 39.0523 73.4865 38.3801C73.2876 37.6897 72.9891 37.181 72.5911 36.854C72.1932 36.5088 71.7138 36.3362 71.153 36.3362C70.5742 36.3362 70.0858 36.5088 69.6878 36.854C69.3079 37.181 69.0094 37.6897 68.7924 38.3801C68.5934 39.0523 68.4939 39.8971 68.4939 40.9144C68.4939 41.9318 68.5934 42.7766 68.7924 43.4488C69.0094 44.121 69.3079 44.6297 69.6878 44.9749C70.0858 45.3019 70.5742 45.4654 71.153 45.4654Z" fill="white"/>
<path d="M80.7042 36.6905H79.1304V34.1016H86.348V36.6905H85.5883L87.4605 44.4026H87.5419L88.9529 39.1704L88.1389 36.6905H87.0264V34.1016H94.6782V36.6905H93.43L95.5464 44.4299H95.655L97.5272 36.6905H96.1163V34.1016H101.869V36.6905H100.268L97.2016 47.7273H92.209L90.4724 41.1597L88.6816 47.7273H83.7432L80.7042 36.6905Z" fill="white"/>
<path d="M1.54373 9.45236H0V6.84954H7.07987V9.45236H6.33462L8.17113 17.206H8.25098L9.63501 11.9456L8.83653 9.45236H7.74527V6.84954H15.251V9.45236H14.0267L16.1027 17.2334H16.2092L18.0457 9.45236H16.6617V6.84954H22.3043V9.45236H20.7339L17.7263 20.5486H12.8289L11.1255 13.9457L9.36885 20.5486H4.52473L1.54373 9.45236Z" fill="white"/>
<path d="M33.531 20.2198C33.1229 20.366 32.5906 20.4938 31.9341 20.6034C31.5614 20.6582 31.18 20.7039 30.7896 20.7404C30.417 20.7952 30.0355 20.8226 29.6451 20.8226C28.2788 20.8226 27.0545 20.5303 25.9721 19.9459C24.8897 19.3614 24.038 18.5394 23.4169 17.48C22.8136 16.4206 22.512 15.1968 22.512 13.8087C22.512 12.384 22.8136 11.1237 23.4169 10.0277C24.038 8.9318 24.8986 8.08246 25.9987 7.4797C27.0988 6.87694 28.3586 6.57556 29.7782 6.57556C31.1977 6.57556 32.4398 6.87694 33.5044 7.4797C34.5868 8.06419 35.4208 8.89527 36.0063 9.97293C36.5919 11.0323 36.8935 12.2652 36.9113 13.6717C36.9113 13.7995 36.9024 13.9365 36.8847 14.0827C36.8847 14.2105 36.8758 14.3475 36.858 14.4936H27.2763C27.3472 15.3886 27.4892 16.1466 27.7021 16.7677C27.9328 17.3887 28.2256 17.8545 28.5805 18.165C28.9353 18.4755 29.3434 18.6307 29.8048 18.6307C30.2129 18.6307 30.6654 18.4755 31.1622 18.165C31.6768 17.8545 32.1204 17.4892 32.493 17.069C32.8834 16.6489 33.1229 16.2745 33.2116 15.9457V15.7813H35.8999V21.4528H33.531V20.2198ZM32.147 12.8497C32.0583 11.9182 31.9075 11.1602 31.6945 10.5757C31.4816 9.97293 31.2066 9.52542 30.8694 9.23318C30.5323 8.94093 30.1419 8.79481 29.6983 8.79481C29.2547 8.79481 28.8644 8.95006 28.5272 9.26058C28.1901 9.55282 27.9151 10.0003 27.7021 10.6031C27.5069 11.2058 27.3739 11.9547 27.3029 12.8497H32.147Z" fill="white"/>
<path d="M47.6199 20.8226C46.7682 20.8226 46.0229 20.6582 45.3841 20.3294C44.7453 19.9824 44.2041 19.471 43.7605 18.7951V20.5486H39.3955V3.97273H37.2662V1.36991H43.9735V8.41123C44.3993 7.80847 44.9139 7.35184 45.5172 7.04133C46.1382 6.73081 46.8391 6.57556 47.6199 6.57556C48.7732 6.57556 49.8113 6.87694 50.734 7.4797C51.6566 8.06419 52.3753 8.89527 52.8899 9.97293C53.4044 11.0506 53.6617 12.2926 53.6617 13.6991C53.6617 15.1055 53.4044 16.3476 52.8899 17.4252C52.3753 18.5029 51.6566 19.3431 50.734 19.9459C49.829 20.5303 48.791 20.8226 47.6199 20.8226ZM46.3423 18.028C46.8214 18.028 47.2295 17.8819 47.5666 17.5896C47.9215 17.2791 48.1966 16.8042 48.3917 16.1649C48.5869 15.5256 48.6845 14.7037 48.6845 13.6991C48.6845 12.6945 48.5869 11.8725 48.3917 11.2332C48.1966 10.5757 47.9215 10.1008 47.5666 9.80854C47.2295 9.51629 46.8214 9.37017 46.3423 9.37017C45.8455 9.37017 45.393 9.56196 44.9849 9.94553C44.5945 10.3108 44.284 10.8223 44.0533 11.4798C43.8404 12.1374 43.7339 12.8771 43.7339 13.6991C43.7339 14.5028 43.8404 15.2334 44.0533 15.8909C44.284 16.5485 44.5945 17.069 44.9849 17.4526C45.393 17.8362 45.8455 18.028 46.3423 18.028Z" fill="white"/>
<path d="M55.9906 12.9867H58.4925V13.1237C58.4925 13.8178 58.7143 14.5758 59.1579 15.3978C59.6193 16.2197 60.2048 16.9138 60.9146 17.48C61.6421 18.028 62.3696 18.302 63.0971 18.302C63.6294 18.302 64.073 18.1558 64.4279 17.8636C64.7828 17.5531 65.0401 17.1421 65.1998 16.6307C65.3772 16.101 65.466 15.4982 65.466 14.8224C65.466 14.0918 65.3683 13.4799 65.1732 12.9867C64.978 12.4936 64.7118 12.1283 64.3747 11.8908C64.0553 11.6351 63.6915 11.5072 63.2834 11.5072C62.9818 11.5072 62.6713 11.5712 62.3519 11.699C62.0502 11.8269 61.7663 12.0095 61.5001 12.247L61.234 12.4936L59.9564 10.7401L65.3861 6.0002H60.7283C60.2669 6.0002 59.9121 6.02759 59.6636 6.08239C59.433 6.13719 59.2555 6.23765 59.1313 6.38377C59.0249 6.52989 58.9716 6.75821 58.9716 7.06872V8.00026H56.4431V0H58.9982V1.36991H70.6295V3.94534L64.2416 9.86334C64.4723 9.77201 64.7296 9.70808 65.0135 9.67155C65.2974 9.61675 65.5635 9.58935 65.812 9.58935C66.7701 9.58935 67.6307 9.80854 68.3937 10.2469C69.1745 10.667 69.7866 11.2698 70.2302 12.0552C70.6738 12.8223 70.8956 13.7082 70.8956 14.7128C70.8956 15.8818 70.6029 16.9321 70.0173 17.8636C69.4317 18.7951 68.5889 19.5349 67.4888 20.0828C66.3886 20.6125 65.0844 20.8774 63.5762 20.8774C63.0084 20.8774 62.4406 20.8135 61.8728 20.6856C61.305 20.5577 60.7993 20.3842 60.3557 20.165C60.0718 20.0737 59.7701 19.9824 59.4507 19.8911C59.1491 19.7815 58.8474 19.6901 58.5458 19.6171V21.3432H55.9906V12.9867Z" fill="white"/>
<path d="M72.3558 23.4254H74.4052V9.45236H72.3558V6.84954H78.7702V8.57562C79.2138 7.91807 79.755 7.4249 80.3938 7.09612C81.0503 6.74908 81.8045 6.57556 82.6562 6.57556C83.8095 6.57556 84.8476 6.8678 85.7703 7.4523C86.693 8.03679 87.4116 8.86787 87.9262 9.94553C88.4407 11.0232 88.698 12.2744 88.698 13.6991C88.698 15.1055 88.4319 16.3476 87.8995 17.4252C87.385 18.5029 86.6663 19.3431 85.7436 19.9459C84.8387 20.5303 83.8095 20.8226 82.6562 20.8226C81.8577 20.8226 81.1568 20.6673 80.5535 20.3568C79.9502 20.0463 79.4356 19.5805 79.0098 18.9595V23.5624H82.3368V26.0282H72.3558V23.4254ZM81.3786 18.028C81.84 18.028 82.2481 17.8819 82.6029 17.5896C82.9578 17.2791 83.224 16.8042 83.4014 16.1649C83.5966 15.5256 83.6942 14.7037 83.6942 13.6991C83.6942 12.6945 83.5966 11.8725 83.4014 11.2332C83.224 10.5757 82.9578 10.1008 82.6029 9.80854C82.2481 9.51629 81.84 9.37017 81.3786 9.37017C80.864 9.37017 80.4027 9.56196 79.9946 9.94553C79.6042 10.3108 79.3025 10.8223 79.0896 11.4798C78.8767 12.1374 78.7702 12.8771 78.7702 13.6991C78.7702 14.5028 78.8767 15.2334 79.0896 15.8909C79.3025 16.5485 79.6042 17.069 79.9946 17.4526C80.4027 17.8362 80.864 18.028 81.3786 18.028Z" fill="white"/>
<path d="M89.7706 17.9458H91.82V9.45236H89.7706V6.84954H96.185V8.71261C96.4512 8.05506 96.8327 7.54362 97.3295 7.17832C97.8264 6.81301 98.3942 6.63035 99.033 6.63035C99.2991 6.63035 99.6096 6.68515 99.9645 6.79474C100.071 6.83127 100.177 6.8678 100.284 6.90434C100.39 6.94087 100.488 6.9774 100.577 7.01393V5.72622H102.946V12.9867H100.657V12.6032C100.657 12.1648 100.515 11.7173 100.231 11.2606C99.9645 10.7857 99.6274 10.393 99.2193 10.0825C98.8289 9.77201 98.474 9.61675 98.1546 9.61675C97.8175 9.61675 97.5158 9.76287 97.2497 10.0551C97.0013 10.3291 96.7972 10.7309 96.6375 11.2606C96.4956 11.7721 96.4246 12.3931 96.4246 13.1237V17.9458H99.6717V20.5486H89.7706V17.9458Z" fill="white"/>
<path d="M104.664 17.9458H106.714V9.45236H104.664V6.84954H111.318V17.9458H113.368V20.5486H104.664V17.9458ZM105.676 0.136991H111.797L108.869 5.61662H108.603L105.676 0.136991Z" fill="white"/>
<path d="M115.704 9.45236H114.054V6.84954H122.598V9.45236H120.788L123.423 16.7129H123.503L126.164 9.45236H123.982V6.84954H130.609V9.45236H128.959L124.647 20.5486H120.069L115.704 9.45236Z" fill="white"/>
<path d="M134.76 20.8226C133.376 20.8226 132.303 20.4847 131.54 19.8089C130.777 19.1148 130.395 18.1924 130.395 17.0417C130.395 15.8179 130.865 14.8681 131.806 14.1922C132.746 13.5164 134.139 13.1055 135.985 12.9593C136.854 12.8863 137.537 12.7675 138.034 12.6032C138.549 12.4388 138.921 12.2104 139.152 11.9182C139.4 11.6077 139.525 11.2058 139.525 10.7127C139.525 10.0369 139.4 9.55282 139.152 9.26058C138.903 8.96833 138.531 8.8222 138.034 8.8222C137.537 8.8222 136.996 8.99573 136.41 9.34277C135.843 9.68981 135.355 10.1191 134.947 10.6305C134.538 11.1419 134.308 11.626 134.255 12.0826V12.3018H131.886V5.9454H134.228V7.12352C134.884 7.05046 135.497 6.95913 136.064 6.84954C136.402 6.75821 136.756 6.69428 137.129 6.65775C137.502 6.60295 137.892 6.57556 138.3 6.57556C140.11 6.57556 141.521 7.06872 142.532 8.05506C143.544 9.04139 144.049 10.4204 144.049 12.1922V17.9458H145.753V20.5486H139.658V18.439C139.143 19.2426 138.469 19.8454 137.635 20.2472C136.819 20.6308 135.86 20.8226 134.76 20.8226ZM136.703 18.2472C137.165 18.2472 137.617 18.0919 138.061 17.7814C138.522 17.4526 138.895 17.0143 139.179 16.4663C139.48 15.9001 139.631 15.2608 139.631 14.5484V13.0963C139.4 13.352 139.108 13.5803 138.753 13.7813C138.398 13.9639 137.963 14.1009 137.448 14.1922C136.721 14.3201 136.171 14.5758 135.798 14.9594C135.426 15.3247 135.239 15.827 135.239 16.4663C135.239 16.996 135.372 17.4252 135.639 17.754C135.905 18.0828 136.26 18.2472 136.703 18.2472Z" fill="white"/>
<path d="M156.917 20.2472C156.527 20.3934 155.986 20.5121 155.294 20.6034C154.921 20.6765 154.549 20.7313 154.176 20.7678C153.803 20.8043 153.431 20.8226 153.058 20.8226C151.674 20.8226 150.432 20.5212 149.332 19.9185C148.232 19.2974 147.371 18.4481 146.75 17.3704C146.147 16.2745 145.845 15.0507 145.845 13.6991C145.845 12.3292 146.147 11.1054 146.75 10.0277C147.371 8.95006 148.232 8.10985 149.332 7.50709C150.432 6.88607 151.674 6.57556 153.058 6.57556C153.715 6.57556 154.389 6.63949 155.081 6.76734C155.72 6.87694 156.243 6.99566 156.651 7.12352V5.9454H159.02L158.994 12.6853H156.518V12.4662C156.465 11.9913 156.252 11.489 155.879 10.9593C155.507 10.4296 155.072 9.98206 154.575 9.61675C154.078 9.25144 153.626 9.06879 153.218 9.06879C152.703 9.06879 152.26 9.24231 151.887 9.58935C151.514 9.9364 151.222 10.457 151.009 11.151C150.814 11.8269 150.716 12.6762 150.716 13.6991C150.716 14.7037 150.814 15.553 151.009 16.2471C151.222 16.9229 151.514 17.4344 151.887 17.7814C152.26 18.1284 152.703 18.302 153.218 18.302C153.661 18.302 154.149 18.1284 154.682 17.7814C155.214 17.4161 155.675 16.9686 156.066 16.4389C156.456 15.9092 156.696 15.4069 156.784 14.932V14.7128H159.26V21.4528H156.917V20.2472Z" fill="white"/>
<path d="M161.602 18.7677H163.944V19.9459C163.944 20.4573 164.121 21.0053 164.476 21.5897C164.831 22.1925 165.257 22.6948 165.754 23.0966C166.268 23.5167 166.73 23.7268 167.138 23.7268C167.351 23.7268 167.528 23.6537 167.67 23.5076C167.812 23.3615 167.883 23.1697 167.883 22.9323V22.9049C167.883 22.7405 167.821 22.503 167.697 22.1925C167.59 21.9003 167.324 21.2975 166.898 20.3842C166.792 20.1468 166.667 19.8911 166.525 19.6171C166.401 19.3431 166.268 19.0509 166.126 18.7403L161.681 9.45236H160.084V6.84954H168.522V9.45236H166.951L169.16 14.795C169.214 14.9411 169.276 15.1055 169.347 15.2882C169.418 15.4526 169.489 15.6261 169.56 15.8087C169.719 16.2106 169.835 16.5302 169.906 16.7677C169.941 16.6033 169.977 16.4572 170.012 16.3293C170.048 16.1832 170.092 16.0279 170.145 15.8635L172.248 9.45236H170.039V6.84954H176.64V9.45236H174.83L171.795 17.5348C171.778 17.5713 171.769 17.6079 171.769 17.6444C171.769 17.6627 171.76 17.6901 171.742 17.7266C171.583 18.1832 171.467 18.6033 171.396 18.9869C171.343 19.3705 171.316 19.7906 171.316 20.2472V20.6856C171.316 21.8729 171.148 22.8866 170.811 23.7268C170.491 24.567 170.03 25.2063 169.427 25.6447C168.823 26.083 168.122 26.3022 167.324 26.3022C166.934 26.3022 166.534 26.2657 166.126 26.1926C165.718 26.1378 165.337 26.0556 164.982 25.9461C164.591 25.8365 164.263 25.6995 163.997 25.5351V26.7954H161.602V18.7677Z" fill="white"/>
<path d="M1.99609 36.2163C1.99609 35.1117 2.89152 34.2163 3.99609 34.2163H35.7614C36.866 34.2163 37.7614 35.1117 37.7614 36.2163V45.9154C37.7614 47.0199 36.866 47.9154 35.7614 47.9154H3.99609C2.89152 47.9154 1.99609 47.0199 1.99609 45.9154V36.2163Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

10
public/no-image-1-1.svg Normal file
View file

@ -0,0 +1,10 @@
<svg width="240" height="240" viewBox="0 0 240 240" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_119_47)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M238.184 0H0V238.895L238.184 0ZM0 239.344V240H0.457031L0 239.344ZM2.01635 240H240V1.30604L2.01635 240Z" fill="#1A1A1A"/>
</g>
<defs>
<clipPath id="clip0_119_47">
<rect width="240" height="240" fill="black"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 419 B

BIN
public/web3privacy_eye.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

3
server/api/data.get.ts Normal file
View file

@ -0,0 +1,3 @@
export default defineEventHandler(() => {
return $fetch('https://explorer-data.web3privacy.info/')
})

4
tsconfig.json Normal file
View file

@ -0,0 +1,4 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}

7
types/audit.ts Normal file
View file

@ -0,0 +1,7 @@
export interface Audit {
name: string
company?: string
logo?: string
link?: string
time?: string
}

5
types/category.ts Normal file
View file

@ -0,0 +1,5 @@
export interface Category {
id: string
name: string
projectsCount: number
}

View file

@ -0,0 +1,4 @@
export interface ClientDiversability {
name: string
link?: string
}

5
types/components.ts Normal file
View file

@ -0,0 +1,5 @@
export interface InputOption {
label: string
value: string
count?: number
}

7
types/fund.ts Normal file
View file

@ -0,0 +1,7 @@
export interface Fund {
name: string
type?: string
link?: string
value?: string
time?: string
}

3
types/index.ts Normal file
View file

@ -0,0 +1,3 @@
export * from './category'
export * from './project'
export * from './components'

19
types/member.ts Normal file
View file

@ -0,0 +1,19 @@
export interface Member {
name: string
role?: string
link?: string
avatar?: string
anonymous?: boolean
teammembers?: {
name?: string
role?: string
link?: string
[k: string]: unknown
}
company?: {
name?: string
link?: string
contacts?: string
[k: string]: unknown
}
}

145
types/project.ts Normal file
View file

@ -0,0 +1,145 @@
import type { Member } from './member'
import type { Fund } from './fund'
import type { ClientDiversability } from './clientDiversability'
import type { Audit } from './audit'
export interface Project {
id: string
name: string
categories: string[]
ecosystem?: string
product_readiness?: string
security?: string
have_token?: boolean
token_link?: string
tokens?: {
name?: string
symbol: string
network?: string
contract_address?: string
link?: string
[k: string]: unknown
}[]
description?: string
project_type?: string
product_launch_day?: string
technology?: {
type: string
name?: string
features: string[]
[k: string]: unknown
}
links?: {
web?: string
twitter?: string
telegram?: string
discord?: string
blog?: string
facebook?: string
block_explorer?: string
whitepaper?: string
github?: string
docs?: string
changelog?: string
forum?: string
snapshot?: string
lens?: string
farcaster?: string
rss_feed?: string
coingecko?: string
token?: string
[k: string]: unknown
}
blockchain_features?: {
p2p?: boolean
encryption?: string
network?: string
upgradability?: {
enabled?: boolean
type?: string
admin_keys?: string
[k: string]: unknown
}
[k: string]: unknown
opensource: boolean
viewing_key: boolean
dissapearing_tx: boolean
frontend_anonymity: string
identity_integration: null
connected_tx: boolean
revealed_recipient: boolean
revealed_sender: boolean
revealed_ammount: boolean
reversability_condition: string
data_masking: string
asset_custody_type: string
}
licences?: string
privacy_policy?: {
defined?: boolean
link?: string
data_usage?: string
[k: string]: unknown
}
team?: Member[]
storage?: {
decentralized?: boolean
[k: string]: unknown
}
tracebility?: {
tracked_data?: string
kyc?: boolean
sign_in_type_requirments?: string
[k: string]: unknown
}
third_party_dependency?: string
compliance?: string
audits?: Audit[]
social_trust?: string
technical_spof?: string
history?: {
title?: string
event_type?: string
description?: string
time?: string
link?: string
[k: string]: unknown
}
client_diversability?: ClientDiversability[]
default_privacy?: boolean
funding?: Fund[]
project_status?: {
live_status?: boolean
version?: string
testnet?: boolean
mainnet?: boolean
[k: string]: unknown
}
logos?: {
file?: string
ext?: string
url?: string
[k: string]: unknown
}[]
}
export interface ProjectShallow {
id: string
image: string
title1: string
description: string
percentage: number
forum?: string | undefined
github?: string | undefined
website?: string | undefined
twitter?: string | undefined
coingecko?: string | undefined
explorer?: string | undefined
newsletter?: string | undefined
readyness?: string | undefined
team: Member[] | undefined
docs: string | undefined
audits?: Audit[] | undefined
support?: number | undefined
anonymity?: boolean | undefined
}

File diff suppressed because one or more lines are too long

60
unocss.config.ts Normal file
View file

@ -0,0 +1,60 @@
import {
defineConfig,
presetAttributify,
presetIcons,
presetUno,
presetWebFonts,
} from 'unocss'
import { collections } from './unocss.config.collections'
export default defineConfig({
shortcuts: [
{
'app-container': 'px-12px sm:px-1.5rem max-w-1400px m-auto w-full',
'custom-link': 'text-app-text-grey hover:underline underline-offset-4px text-18px font-400 leading-32px cursor-pointer',
},
{
'custom-link-active': 'text-app-white! underline',
},
],
presets: [
presetUno(),
presetAttributify(),
presetIcons({
warn: true,
customizations: {
transform(svg, collection) {
if (collection === 'web') {
svg = svg.replace(/stroke="#[a-zA-Z0-9]+"/, 'stroke="currentColor"')
svg = svg.replace(/fill="#[a-zA-Z0-9]+"/, 'fill="currentColor"')
}
return svg
},
},
collections,
}),
presetWebFonts({
provider: 'google',
fonts: {
archivo: 'Archivo',
},
}),
],
theme: {
colors: {
app: {
white: '#fff',
black: '#000',
green: '#B5E26B',
red: '#FF5252',
bg: {
grey: '#ffffff33',
dark_grey: '#161616',
},
text: {
grey: '#909090',
},
},
},
},
})