mirror of
https://github.com/web3privacy/explorer-app.git
synced 2024-10-15 16:46:26 +02:00
feat: form & github api
This commit is contained in:
parent
c249e433bc
commit
241776eaf1
43 changed files with 3521 additions and 128 deletions
35
app.config.ts
Normal file
35
app.config.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
export default defineAppConfig({
|
||||
github: {
|
||||
appId: 995628,
|
||||
privateKey: `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwkAjU2VY6eUsWm9VrbYyEGZRtY5aSBjWv2O2LvE71F5qOb1E
|
||||
BcXAOVLFP3wU3+y4Nu9Qz9eKumALmKKokzd6KGrVyiIvCOjA8YtzKpHKa24a/TqH
|
||||
ek6z1OvyAH+Zk+PoRGY5KU4YD17Hb81aysO6umI7Hn7NKzuDLHqmE2SmrzXeT7iQ
|
||||
n0+yfwvJPeT+LyCxF42RgPSAzEWhf1Ng/v1neMyr6dfKPKD93KSJJ3rJd1rVZNlY
|
||||
vMAfzR8kmfFEoQqBjtyW+OdTURSTQtjm+wsD+qGT4560mU22ZIY1yzNugsiSKdIG
|
||||
bZR7uLC6Ljg7o0TH+HUCwm3w3N/V8EcWq6AIkQIDAQABAoIBAHR9jjHHB2lM/CpH
|
||||
tjP0nVVkZv/gHJg7/RzNqUXBeUXEUSPLMu3bR4mY751Axiv7hAW9wtLByfo2+v1Q
|
||||
HpQaovF2EDumAFrxajPwkuImiCOg5iKSZQieBg5caMaPvwQHmxTZ0NHKeXdOOX/8
|
||||
wMP0WRjJK/b/x/5zNBkKfGoI5EURlyC7Q6wlmW6DGnFPjy7vC7ZKWj1dKfyNK8ZJ
|
||||
T3absmwGva7cJd3a8nQ1DKK+Tl3RmYoLQpdXPfWDV9/omiSE+EcR0eIm5/v8vQgE
|
||||
UMGv9fPAXo9D5pgEaGqV5zekLUKH2W8xt9pvHDnrjoGSEWGjT88xyJG37DRXa+wR
|
||||
quLZ36ECgYEA648dDFMYyhThxbamtzOFCgBM1FYZGSV4618OIvTeQquuKtWjHnMV
|
||||
2kvl5iUsmLALCLMJeCc8B7Hc4gZsYcm8VrvhvVsv8P1w6ea+wO/g25Q1hKwf9y2k
|
||||
xdgDomAZxrJ7hXy57JTTZBvvUFOdok7jgCQAGWQg4zXTzoR1Hf1sInUCgYEA0xtd
|
||||
xxRiVKh1Ei5sRgdUxE1rixRFhEZCdpu8ha1mC3cDlCCiie3ia4VWAkDN7eU8V0gX
|
||||
DfD4CrxLThWRWlivfT9uhsncXibza9b+QIc0yluReM4+iswA5bd429cZx6kvivgj
|
||||
FtYd0QKyHduxyAjUfB5JAtO2jJN4J3IWyd6y0i0CgYEAwwXKw/BsXqqpPZr6LkUu
|
||||
SVh0Q3xA7UvT7/LT7mcTONmQqhAUK9qWZhRec4ulf4iIqhwoo9y/25MLT+qHgvKV
|
||||
xq1ouPAtIfWCrXSHy48OeF0dbglfsbKNb+tnHuE3lgUhNSRNfiy9U225VaTUppNv
|
||||
0SB9IIiAnfxrrtiFal3tUB0CgYEAt4bwFRXMkorNFFxVduACupIP755U/TTTrSeh
|
||||
Upm3wDncW5evlre/gvgtGNJb9Pi4Z6zdlhoqmbKuyRiJAAFft0RBA19UfGnib5s9
|
||||
+L1SkKXgpySuTJ4kHZFOudibqe+UzH0CjGTjEQUF8l0/hobeq3Tsxk9mQXonGCvg
|
||||
UemQjpUCgYAvooI/aHrWyFZTigM63W5py4e6YMRvrvZbCBu4qougpJ4TQDnG2tMf
|
||||
qW6A1gT6hIC54kGOn6WuO20CVVTZpImEwUYQchsvhsKVAxjp3dq8fw1KIuCPrZAs
|
||||
u1mh4iVpLYIyyyeazf2RhFXBNU49DIS8xYndO970Q9pHZhz629f+qA==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`,
|
||||
installationId: 54778330,
|
||||
},
|
||||
})
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
border?: boolean
|
||||
invertedColor?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
@ -8,14 +9,14 @@ defineProps<{
|
|||
<div
|
||||
flex
|
||||
items-center
|
||||
justify-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
|
||||
:class="[border ? 'border-2px border-app-white' : 'border-0',
|
||||
invertedColor ? 'bg-app-white text-app-black hover:text-app-white hover:bg-app-black' : 'text-app-white bg-app-black hover:text-app-black hover:bg-app-white',
|
||||
]"
|
||||
>
|
||||
<template v-if="!!$slots.prefix">
|
||||
<div text-24px>
|
||||
|
|
|
@ -27,7 +27,7 @@ function onOptionSelected(value: string) {
|
|||
<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="block truncate mr-8px">{{ isOptionSelected?.label }} <span v-if="isOptionSelected?.count" 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
|
||||
|
@ -61,7 +61,7 @@ function onOptionSelected(value: string) {
|
|||
:class="[selected ? 'font-semibold' : 'font-normal']"
|
||||
>
|
||||
{{ option.label }}
|
||||
<span opacity-50>({{ option.count }})</span>
|
||||
<span v-if="option.count" opacity-50>({{ option.count }})</span>
|
||||
</span>
|
||||
</li>
|
||||
</HeadlessListboxOption>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
to: string
|
||||
to?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ watch(y, (newY, oldY) => {
|
|||
lg:bg-app-bg-dark_grey
|
||||
>
|
||||
<div
|
||||
v-if="!isProjectRoute"
|
||||
v-if="!isProjectRoute && !route.fullPath.includes('create') && !route.fullPath.includes('edit')"
|
||||
relative
|
||||
app-container
|
||||
w-full
|
||||
|
@ -355,7 +355,10 @@ watch(y, (newY, oldY) => {
|
|||
:class="[(!showBar && width < 768) ? 'translate-y--128px' : 'translate-y-0px']"
|
||||
duration-200ms
|
||||
>
|
||||
<ProjectNavigation md:mt-16px />
|
||||
<ProjectNavigation
|
||||
v-if="!route.fullPath.includes('create') && !route.fullPath.includes('edit')"
|
||||
md:mt-16px
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
|
|
181
components/Project/Create/Categories/Assets.vue
Normal file
181
components/Project/Create/Categories/Assets.vue
Normal file
|
@ -0,0 +1,181 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
type Token = {
|
||||
name: string
|
||||
symbol: string
|
||||
contract_address: string
|
||||
token_link: string
|
||||
}
|
||||
|
||||
function useTokens(project?: Partial<Project>) {
|
||||
const tokens = ref<Token[]>(project?.tokens as Token[] || [])
|
||||
const newToken = reactive<Token>({
|
||||
symbol: '',
|
||||
name: '',
|
||||
contract_address: '',
|
||||
token_link: '',
|
||||
})
|
||||
|
||||
function addToken() {
|
||||
tokens.value.push({ ...newToken })
|
||||
newToken.symbol = ''
|
||||
newToken.name = ''
|
||||
newToken.contract_address = ''
|
||||
newToken.token_link = ''
|
||||
}
|
||||
|
||||
function removeToken(index: number) {
|
||||
tokens.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
tokens,
|
||||
newToken,
|
||||
addToken,
|
||||
removeToken,
|
||||
}
|
||||
}
|
||||
|
||||
const { tokens, newToken, addToken, removeToken } = useTokens(props.project)
|
||||
const assetsUsed = ref<string[]>(Array.isArray(props.project?.assets_used) ? props.project?.assets_used.map(a => a.toLowerCase()) : [])
|
||||
|
||||
const { useProject, assetsData } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
assets_used: assetsUsed.value,
|
||||
have_token: tokens.value.length > 0 && tokens.value.some(token => token.contract_address),
|
||||
tokens: tokens.value,
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-24px>
|
||||
<ClientOnly>
|
||||
<ProjectCreateComponentsSelectChips
|
||||
v-model="assetsUsed"
|
||||
label="Assets used"
|
||||
:options="assetsData?.map(asset => ({ label: asset.name, value: asset.id }))"
|
||||
placeholder="Add assets"
|
||||
hint="Most used assets user can use to interact with your project"
|
||||
can-add-new
|
||||
/>
|
||||
</ClientOnly>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="NATIVE TOKENS"
|
||||
/>
|
||||
<ProjectCreateComponentsItem
|
||||
v-for="token in tokens"
|
||||
:key="token.name"
|
||||
@remove="() => removeToken(tokens.indexOf(token))"
|
||||
>
|
||||
<template #label>
|
||||
<div
|
||||
flex
|
||||
gap-2px
|
||||
>
|
||||
<span
|
||||
class="text-app-black text-14px font-700"
|
||||
lg="text-16px"
|
||||
> {{ token.symbol }}
|
||||
</span>
|
||||
<span
|
||||
v-if="token.name"
|
||||
class="text-app-black text-14px font-400"
|
||||
lg="text-16px"
|
||||
> {{ `(${token.name})` }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #desc>
|
||||
<NuxtLink
|
||||
target="_blank"
|
||||
:to="token.token_link"
|
||||
hover:text-app-black
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>Etherscan
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</ProjectCreateComponentsItem>
|
||||
<ProjectCreateComponentsItemAdd
|
||||
button-label="ADD TOKEN"
|
||||
@add="addToken()"
|
||||
>
|
||||
<template #content>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="gap-24px"
|
||||
>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
lg="flex-row gap-24px"
|
||||
>
|
||||
<div
|
||||
class="w-100%"
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="flex-row gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newToken.symbol"
|
||||
w-full
|
||||
label="Token symbol"
|
||||
placeholder="Token symbol"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newToken.name"
|
||||
w-full
|
||||
label="Token name"
|
||||
placeholder="Token name"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="w-100%"
|
||||
lg="mt-32px left-1/2 self-center"
|
||||
font-400
|
||||
italic
|
||||
lg:text-14px
|
||||
text-12px
|
||||
text-app-white
|
||||
opacity-50
|
||||
>
|
||||
{{ 'Does your project have native token? Enter Symbol, name and address' }}
|
||||
</span>
|
||||
</div>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newToken.contract_address"
|
||||
lg="w-1/2"
|
||||
label="Token contract address"
|
||||
placeholder="Contract address"
|
||||
hint="Enter token contract address"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newToken.token_link"
|
||||
lg="w-1/2"
|
||||
label="URL for explorer"
|
||||
placeholder="Token explorer URL"
|
||||
hint="Link to any explorer showing data about your native token"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ProjectCreateComponentsItemAdd>
|
||||
</div>
|
||||
</template>
|
130
components/Project/Create/Categories/BasicInfo.vue
Normal file
130
components/Project/Create/Categories/BasicInfo.vue
Normal file
|
@ -0,0 +1,130 @@
|
|||
<script setup lang="ts">
|
||||
import * as yup from 'yup'
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
const { useProject, categoriesData, usecasesData, ecosystemsData } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
const validationSchema = yup.object().shape({
|
||||
categories: yup.array().of(yup.string()).required().min(1),
|
||||
usecases: yup.array().of(yup.string()).required().min(1),
|
||||
ecosystems: yup.array().of(yup.string()).required().min(1),
|
||||
description: yup.string().required(),
|
||||
})
|
||||
|
||||
const { validate, meta, resetForm } = useForm({
|
||||
validationSchema,
|
||||
})
|
||||
|
||||
const { value: categories, errorMessage: categoriesError } = useField<string[]>('categories')
|
||||
const { value: usecases, errorMessage: usecasesError } = useField<string[]>('usecases')
|
||||
const { value: ecosystems, errorMessage: ecosystemsError } = useField<string[]>('ecosystems')
|
||||
const { value: description, errorMessage: descriptionError } = useField<string>('description')
|
||||
const day = ref(new Date(props.project?.product_launch_day || '').getDay())
|
||||
const month = ref(new Date(props.project?.product_launch_day || '').getMonth())
|
||||
const year = ref(new Date(props.project?.product_launch_day || '').getFullYear())
|
||||
const isDead = ref(props.project?.sunset || false)
|
||||
|
||||
resetForm({
|
||||
values: {
|
||||
categories: props.project?.categories?.map(c => c.toLowerCase()) || [],
|
||||
usecases: props.project?.usecases?.map(u => u.toLowerCase()) || [],
|
||||
ecosystems: Array.isArray(props.project?.ecosystem) ? props.project?.ecosystem?.map(e => e.toLowerCase()) : [],
|
||||
description: props.project?.description || '',
|
||||
},
|
||||
})
|
||||
|
||||
function isFormValid() {
|
||||
validate()
|
||||
|
||||
if (meta.value.valid) {
|
||||
return true
|
||||
}
|
||||
else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
categories: categories.value,
|
||||
usecases: usecases.value,
|
||||
ecosystem: ecosystems.value,
|
||||
description: description.value,
|
||||
product_launch_day: new Date(year.value, month.value, day.value).toISOString(),
|
||||
sunset: isDead.value,
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
isFormValid,
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-24px>
|
||||
<ClientOnly>
|
||||
<ProjectCreateComponentsSelectChips
|
||||
v-model="categories"
|
||||
label="Categories"
|
||||
required
|
||||
:options="categoriesData?.map(cat => ({ label: cat.name, value: cat.id }))"
|
||||
placeholder="Add category"
|
||||
hint="Choose categories that fits your project"
|
||||
:error="categoriesError"
|
||||
/>
|
||||
<ProjectCreateComponentsSelectChips
|
||||
v-model="usecases"
|
||||
label="Use-cases"
|
||||
required
|
||||
:options="usecasesData?.map(uc => ({ label: uc.name, value: uc.id }))"
|
||||
placeholder="Add use-case"
|
||||
hint="What can be your project used for?"
|
||||
:error="usecasesError"
|
||||
/>
|
||||
<ProjectCreateComponentsSelectChips
|
||||
v-model="ecosystems"
|
||||
label="Ecosystems"
|
||||
required
|
||||
:options="ecosystemsData?.map(ec => ({ label: ec.name, value: ec.id }))"
|
||||
placeholder="Add ecosystem"
|
||||
hint="Choose ecosystems that is your project part of"
|
||||
:error="ecosystemsError"
|
||||
/>
|
||||
</ClientOnly>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="description"
|
||||
lg="w-1/2"
|
||||
label="Description"
|
||||
text-area
|
||||
required
|
||||
hint="What kind of technology you use, what are your special features
|
||||
and why should user use your project."
|
||||
placeholder="Write something about your project"
|
||||
:error="descriptionError"
|
||||
/>
|
||||
<ProjectCreateComponentsDatePicker
|
||||
v-model:day="day"
|
||||
v-model:month="month"
|
||||
v-model:year="year"
|
||||
label="Project launch date"
|
||||
hint="Date of project emergence (Optional)"
|
||||
/>
|
||||
<span
|
||||
text-16px
|
||||
font-400
|
||||
lg="text-16px"
|
||||
>Other Information
|
||||
</span>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="isDead"
|
||||
label="Sunset (project is dead)"
|
||||
hint="Check if project is currently running and working"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
116
components/Project/Create/Categories/Funding.vue
Normal file
116
components/Project/Create/Categories/Funding.vue
Normal file
|
@ -0,0 +1,116 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
type Fundings = {
|
||||
name: string
|
||||
link: string
|
||||
}
|
||||
|
||||
function useFundings(project?: Partial<Project>) {
|
||||
const fundings = ref<Fundings[]>(project?.funding as Fundings[] || [])
|
||||
const newFunding = reactive<Fundings>({
|
||||
name: '',
|
||||
link: '',
|
||||
})
|
||||
|
||||
function addFunding() {
|
||||
fundings.value.push({ ...newFunding })
|
||||
newFunding.name = ''
|
||||
newFunding.link = ''
|
||||
}
|
||||
|
||||
function removeFunding(index: number) {
|
||||
fundings.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
fundings,
|
||||
newFunding,
|
||||
addFunding,
|
||||
removeFunding,
|
||||
}
|
||||
}
|
||||
|
||||
const { fundings, newFunding, addFunding, removeFunding } = useFundings(props.project)
|
||||
|
||||
const { useProject } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
funding: fundings.value,
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="FUNDING"
|
||||
/>
|
||||
<ProjectCreateComponentsItem
|
||||
v-for="funding in fundings"
|
||||
:key="funding.name"
|
||||
@remove="() => removeFunding(fundings.indexOf(funding))"
|
||||
>
|
||||
<template #label>
|
||||
<div
|
||||
flex
|
||||
gap-2px
|
||||
>
|
||||
<span
|
||||
class="text-app-black text-14px font-700"
|
||||
lg="text-16px"
|
||||
> {{ funding.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #desc>
|
||||
<NuxtLink
|
||||
:to="funding.link"
|
||||
hover:text-app-black
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>Link
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</ProjectCreateComponentsItem>
|
||||
<ProjectCreateComponentsItemAdd
|
||||
button-label="ADD FUNDING"
|
||||
@add="addFunding()"
|
||||
>
|
||||
<template #content>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newFunding.name"
|
||||
lg="w-50%"
|
||||
label="Name"
|
||||
placeholder="Investor's name"
|
||||
hint="Name of entity investing into project"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newFunding.link"
|
||||
lg="w-50%"
|
||||
label="Link confirming investment"
|
||||
placeholder="Add URL"
|
||||
hint="Link to Article, PR or other sources confirming funding information"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ProjectCreateComponentsItemAdd>
|
||||
</div>
|
||||
</template>
|
166
components/Project/Create/Categories/History.vue
Normal file
166
components/Project/Create/Categories/History.vue
Normal file
|
@ -0,0 +1,166 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
type Event = {
|
||||
title: string
|
||||
description: string
|
||||
link: string
|
||||
time: string
|
||||
}
|
||||
|
||||
function useEvents(project?: Partial<Project>) {
|
||||
const events = ref<Event[]>(project?.history as Event[] || [])
|
||||
const newEvent = reactive<Event>({
|
||||
title: '',
|
||||
description: '',
|
||||
link: '',
|
||||
time: '',
|
||||
})
|
||||
|
||||
const day = ref<number>()
|
||||
const month = ref<number>()
|
||||
const year = ref<number>()
|
||||
|
||||
function addEvent() {
|
||||
events.value.push({ ...newEvent })
|
||||
newEvent.title = ''
|
||||
newEvent.description = ''
|
||||
newEvent.link = ''
|
||||
newEvent.time = ''
|
||||
day.value = undefined
|
||||
month.value = undefined
|
||||
year.value = undefined
|
||||
}
|
||||
|
||||
function removeEvent(index: number) {
|
||||
events.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
events,
|
||||
newEvent,
|
||||
day,
|
||||
month,
|
||||
year,
|
||||
addEvent,
|
||||
removeEvent,
|
||||
}
|
||||
}
|
||||
|
||||
const { events, newEvent, addEvent, removeEvent, day, month, year } = useEvents(props.project)
|
||||
|
||||
watch([day, month, year], () => {
|
||||
const yearValue = year.value
|
||||
const monthValue = month.value
|
||||
const dayValue = day.value
|
||||
|
||||
if (!yearValue || !monthValue || !dayValue) {
|
||||
return null
|
||||
}
|
||||
newEvent.time = new Date(yearValue, monthValue - 1, dayValue).toString()
|
||||
})
|
||||
|
||||
function formatDate(date: string) {
|
||||
const d = new Date(date)
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const year = d.getFullYear()
|
||||
return `${day}/${month}/${year}`
|
||||
}
|
||||
|
||||
const { useProject } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
history: events.value,
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-24px>
|
||||
<ProjectCreateComponentsItem
|
||||
v-for="event in events"
|
||||
:key="event.title"
|
||||
@remove="() => removeEvent(events.indexOf(event))"
|
||||
>
|
||||
<template #label>
|
||||
<div
|
||||
flex
|
||||
gap-2px
|
||||
>
|
||||
<span
|
||||
class="text-app-black text-14px font-700"
|
||||
lg="text-16px"
|
||||
> {{ event.title }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #desc>
|
||||
<span
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>{{ formatDate(event.time) }}
|
||||
</span>
|
||||
<NuxtLink
|
||||
:to="event.link"
|
||||
hover:text-app-black
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>Link
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</ProjectCreateComponentsItem>
|
||||
<ProjectCreateComponentsItemAdd
|
||||
button-label="ADD EVENT"
|
||||
@add="addEvent()"
|
||||
>
|
||||
<template #content>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newEvent.title"
|
||||
lg="w-50%"
|
||||
label="Event title"
|
||||
placeholder="Enter title of event"
|
||||
hint="Specific name of events (or what happened)"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newEvent.description"
|
||||
lg="w-50%"
|
||||
label="Event description"
|
||||
placeholder="Describe this event"
|
||||
hint="Detailed description of event (max. 1000 glyphs)"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newEvent.link"
|
||||
lg="w-50%"
|
||||
label="Event URL"
|
||||
placeholder="Enter URL with event details"
|
||||
hint="Link to Article, PR or other sources confirming event information"
|
||||
/>
|
||||
<ProjectCreateComponentsDatePicker
|
||||
v-model:day="day"
|
||||
v-model:month="month"
|
||||
v-model:year="year"
|
||||
label="Date of event"
|
||||
placeholder="When this event happened?"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ProjectCreateComponentsItemAdd>
|
||||
</div>
|
||||
</template>
|
160
components/Project/Create/Categories/Links.vue
Normal file
160
components/Project/Create/Categories/Links.vue
Normal file
|
@ -0,0 +1,160 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
const web = ref<string>(props.project?.links?.web || '')
|
||||
const blog = ref<string>(props.project?.links?.blog || '')
|
||||
const github = ref<string>(props.project?.links?.github || '')
|
||||
const forum = ref<string>(props.project?.links?.forum || '')
|
||||
const docs = ref<string>(props.project?.links?.docs || '')
|
||||
const whitepaper = ref<string>(props.project?.links?.whitepaper || '')
|
||||
const block_explorer = ref<string>(props.project?.links?.block_explorer || '')
|
||||
const governance = ref<string>(props.project?.links?.governance || '')
|
||||
const twitter = ref<string>(props.project?.links?.twitter || '')
|
||||
const discord = ref<string>(props.project?.links?.discord || '')
|
||||
const telegram = ref<string>(props.project?.links?.telegram || '')
|
||||
const lens = ref<string>(props.project?.links?.lens || '')
|
||||
const farcaster = ref<string>(props.project?.links?.farcaster || '')
|
||||
|
||||
const { useProject } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
links: {
|
||||
web: web.value,
|
||||
blog: blog.value,
|
||||
github: github.value,
|
||||
forum: forum.value,
|
||||
docs: docs.value,
|
||||
whitepaper: whitepaper.value,
|
||||
block_explorer: block_explorer.value,
|
||||
governance: governance.value,
|
||||
twitter: twitter.value,
|
||||
discord: discord.value,
|
||||
telegram: telegram.value,
|
||||
lens: lens.value,
|
||||
farcaster: farcaster.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-24px>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="grid grid-cols-2 gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="web"
|
||||
w-full
|
||||
label="Website"
|
||||
placeholder="ex. https://www.yourproject.com"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="blog"
|
||||
w-full
|
||||
label="Blog"
|
||||
placeholder="https://blog,yourproject.com"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="github"
|
||||
w-full
|
||||
label="Github repository"
|
||||
placeholder="Github.com/Yourproject"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="forum"
|
||||
w-full
|
||||
label="Forum"
|
||||
placeholder="https://forum,yourproject.com"
|
||||
/>
|
||||
</div>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="TECHNOGY DETAILS"
|
||||
/>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="grid grid-cols-2 gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="docs"
|
||||
w-full
|
||||
label="Documentation"
|
||||
placeholder="https://docs.yourproject.com"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="whitepaper"
|
||||
w-full
|
||||
label="Whitepaper"
|
||||
placeholder="https://www.yourproject.com/whitepaper.pdf"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="block_explorer"
|
||||
w-full
|
||||
label="Explorer"
|
||||
placeholder="https://explorer.yourproject.com"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="governance"
|
||||
w-full
|
||||
label="Governance"
|
||||
placeholder="https://governance.yourproject.com"
|
||||
/>
|
||||
</div>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="SOCIAL NETWORKS"
|
||||
/>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="grid grid-cols-2 gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="twitter"
|
||||
w-full
|
||||
label="Twitter"
|
||||
placeholder="Twitter profile URL"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="discord"
|
||||
w-full
|
||||
label="Discord"
|
||||
placeholder="Discord invite link"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="telegram"
|
||||
w-full
|
||||
label="Telegram"
|
||||
placeholder="Telegram channel URL"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="lens"
|
||||
w-full
|
||||
label="Lens"
|
||||
placeholder="Lens profile URL"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="farcaster"
|
||||
w-full
|
||||
label="Farcaster"
|
||||
placeholder="Farcaster profile URL"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
104
components/Project/Create/Categories/Privacy.vue
Normal file
104
components/Project/Create/Categories/Privacy.vue
Normal file
|
@ -0,0 +1,104 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
const privacyPolicyUrl = ref(props.project?.privacy_policy?.link || '')
|
||||
const compliance = ref(props.project?.compliance || '')
|
||||
const kyc = ref(props.project?.tracebility?.kyc || false)
|
||||
const defaultPrivacy = ref(props.project?.default_privacy || false)
|
||||
const signRequirements = ref(Array.isArray(props.project?.tracebility?.sign_in_type_requirments) ? props.project?.tracebility?.sign_in_type_requirments?.map(s => s.toLowerCase()) : [])
|
||||
const trackedData = ref(props.project?.tracebility?.tracked_data || '')
|
||||
const dataUsage = ref(props.project?.privacy_policy?.data_usage || '')
|
||||
|
||||
const { useProject, signInRequirmentsData } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
privacy_policy: {
|
||||
defined: privacyPolicyUrl.value ? true : false,
|
||||
link: privacyPolicyUrl.value,
|
||||
data_usage: dataUsage.value,
|
||||
},
|
||||
compliance: compliance.value,
|
||||
tracebility: {
|
||||
kyc: kyc.value,
|
||||
tracked_data: trackedData.value,
|
||||
},
|
||||
default_privacy: defaultPrivacy.value,
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-24px>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-24px
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="privacyPolicyUrl"
|
||||
lg="w-50%"
|
||||
label="Link to project’s Privacy Policy"
|
||||
placeholder="https://policy.yourproject.com"
|
||||
hint="URL of document defining you privacy policy and data usage"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="compliance"
|
||||
lg="w-50%"
|
||||
label="Compliance"
|
||||
placeholder="Enter compliance that project follow"
|
||||
hint="Is project Compliant with regulations? (ex. OFAC, Privacy Act)"
|
||||
/>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="kyc"
|
||||
label="KYC (Know your customer)"
|
||||
hint="Is project requiering KYC for its users?"
|
||||
/>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="defaultPrivacy"
|
||||
label="Defult privacy"
|
||||
hint="Is maximum privacy turned on by default,"
|
||||
/>
|
||||
</div>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="ADDITIONAL INFO"
|
||||
/>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-24px
|
||||
>
|
||||
<ProjectCreateComponentsSelectChips
|
||||
v-model="signRequirements"
|
||||
label="Sign-in requirements"
|
||||
:options="signInRequirmentsData.map(s => ({ label: s.name, value: s.id }))"
|
||||
placeholder="Add requirement"
|
||||
hint="What do you need to provide to use your project?"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="trackedData"
|
||||
lg="w-50%"
|
||||
label="Tracked data"
|
||||
placeholder="Enter tracked data"
|
||||
hint="What data does project collect? (ex. IP, Location, E-mail, Wallet,...)"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="dataUsage"
|
||||
lg="w-50%"
|
||||
label="Data usage"
|
||||
placeholder="What are you using data for?"
|
||||
hint="How does project use user info (resseling, information gathering, none,...)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
199
components/Project/Create/Categories/Security.vue
Normal file
199
components/Project/Create/Categories/Security.vue
Normal file
|
@ -0,0 +1,199 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
type Audit = {
|
||||
name: string
|
||||
url: string
|
||||
time: string
|
||||
}
|
||||
|
||||
function useAudits(project?: Partial<Project>) {
|
||||
const audits = ref<Audit[]>(project?.audits as Audit[] || [])
|
||||
const newAudit = reactive<Audit>({
|
||||
name: '',
|
||||
url: '',
|
||||
time: '',
|
||||
})
|
||||
|
||||
const day = ref<number>()
|
||||
const month = ref<number>()
|
||||
const year = ref<number>()
|
||||
|
||||
function addAudit() {
|
||||
audits.value.push({ ...newAudit })
|
||||
newAudit.name = ''
|
||||
newAudit.url = ''
|
||||
newAudit.time = ''
|
||||
day.value = undefined
|
||||
month.value = undefined
|
||||
year.value = undefined
|
||||
}
|
||||
|
||||
function removeAudit(index: number) {
|
||||
audits.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
audits,
|
||||
newAudit,
|
||||
day,
|
||||
month,
|
||||
year,
|
||||
addAudit,
|
||||
removeAudit,
|
||||
}
|
||||
}
|
||||
|
||||
const { audits, newAudit, addAudit, removeAudit, day, month, year } = useAudits(props.project)
|
||||
|
||||
watch([day, month, year], () => {
|
||||
const yearValue = year.value
|
||||
const monthValue = month.value
|
||||
const dayValue = day.value
|
||||
|
||||
if (!yearValue || !monthValue || !dayValue) {
|
||||
return null
|
||||
}
|
||||
newAudit.time = new Date(yearValue, monthValue - 1, dayValue).toString()
|
||||
})
|
||||
|
||||
function formatDate(date: string) {
|
||||
const d = new Date(date)
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const year = d.getFullYear()
|
||||
return `${day}/${month}/${year}`
|
||||
}
|
||||
|
||||
const thirdPartyDep = ref(props.project?.third_party_dependency || '')
|
||||
const socialTrust = ref(props.project?.social_trust || '')
|
||||
const spof = ref(props.project?.technical_spof || '')
|
||||
|
||||
const { useProject } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
audits: audits.value,
|
||||
third_party_dependency: thirdPartyDep.value,
|
||||
social_trust: socialTrust.value,
|
||||
technical_spof: spof.value,
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="AUDITS"
|
||||
/>
|
||||
<ProjectCreateComponentsItem
|
||||
v-for="audit in audits"
|
||||
:key="audit.name"
|
||||
@remove="() => removeAudit(audits.indexOf(audit))"
|
||||
>
|
||||
<template #label>
|
||||
<div
|
||||
flex
|
||||
gap-2px
|
||||
>
|
||||
<span
|
||||
class="text-app-black text-14px font-700"
|
||||
lg="text-16px"
|
||||
> {{ audit.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #desc>
|
||||
<span
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>{{ formatDate(audit.time) }}
|
||||
</span>
|
||||
<NuxtLink
|
||||
target="_blank"
|
||||
:to="audit.url"
|
||||
hover:text-app-black
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>Link
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</ProjectCreateComponentsItem>
|
||||
<ProjectCreateComponentsItemAdd
|
||||
button-label="ADD AUDIT"
|
||||
@add="addAudit()"
|
||||
>
|
||||
<template #content>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newAudit.name"
|
||||
lg="w-50%"
|
||||
label="Audit name"
|
||||
placeholder="Audit name"
|
||||
hint="Title and Name of audit company"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newAudit.url"
|
||||
lg="w-50%"
|
||||
label="URL of audit"
|
||||
placeholder="URL of audit"
|
||||
hint="Enter URL of audit"
|
||||
/>
|
||||
<ProjectCreateComponentsDatePicker
|
||||
v-model:day="day"
|
||||
v-model:month="month"
|
||||
v-model:year="year"
|
||||
label="Date of audit"
|
||||
placeholder="When has been project audited?"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ProjectCreateComponentsItemAdd>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="ADDITIONAL INFO"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="thirdPartyDep"
|
||||
textarea
|
||||
:textarea-rows="3"
|
||||
lg="w-50%"
|
||||
label="Third party dependency"
|
||||
placeholder="Write about dependencies"
|
||||
hint="Is your project dependend on third service like Uniswap pools, Network or third party contract, Chainlink oracle,...?"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="socialTrust"
|
||||
textarea
|
||||
:textarea-rows="3"
|
||||
lg="w-50%"
|
||||
label="Social trust"
|
||||
placeholder="Who does project trust with treasury and security?"
|
||||
hint="Who is governing project and how? DAO, Multisig wallet of X,"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="spof"
|
||||
textarea
|
||||
:textarea-rows="3"
|
||||
lg="w-50%"
|
||||
label="Single point of failure"
|
||||
placeholder="What is single point of failture for project"
|
||||
hint="What have to happen to shutdown, hack project or leak data? ex. Hack of SHA256, Stolen admin keys,..."
|
||||
/>
|
||||
</div>
|
||||
</template>
|
127
components/Project/Create/Categories/Team.vue
Normal file
127
components/Project/Create/Categories/Team.vue
Normal file
|
@ -0,0 +1,127 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
const isAnonymousTeam = ref<boolean>(props.project?.team?.anonymous || false)
|
||||
|
||||
type Member = {
|
||||
name: string
|
||||
link: string
|
||||
}
|
||||
|
||||
function useMembers(project?: Partial<Project>) {
|
||||
const members = ref<Member[]>(project?.team?.teammembers as Member[] || [])
|
||||
const newMember = reactive<Member>({
|
||||
name: '',
|
||||
link: '',
|
||||
})
|
||||
|
||||
function addMember() {
|
||||
members.value.push({ ...newMember })
|
||||
newMember.name = ''
|
||||
newMember.link = ''
|
||||
}
|
||||
|
||||
function removeMember(index: number) {
|
||||
members.value.splice(index, 1)
|
||||
}
|
||||
|
||||
return {
|
||||
members,
|
||||
newMember,
|
||||
addMember,
|
||||
removeMember,
|
||||
}
|
||||
}
|
||||
|
||||
const { members, newMember, addMember, removeMember } = useMembers(props.project)
|
||||
|
||||
const { useProject } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
team: {
|
||||
anonymous: isAnonymousTeam.value,
|
||||
teammembers: members.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div mt-24px>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="isAnonymousTeam"
|
||||
label="Anonymous team"
|
||||
hint="Are developers able to upgrade and change deployed contract?"
|
||||
/>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="TEAM MEMBERS"
|
||||
/>
|
||||
<ProjectCreateComponentsItem
|
||||
v-for="member in members"
|
||||
:key="member.name"
|
||||
@remove="() => removeMember(members.indexOf(member))"
|
||||
>
|
||||
<template #label>
|
||||
<div
|
||||
flex
|
||||
gap-2px
|
||||
>
|
||||
<span
|
||||
class="text-app-black text-14px font-700"
|
||||
lg="text-16px"
|
||||
> {{ member.name }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #desc>
|
||||
<NuxtLink
|
||||
target="_blank"
|
||||
:to="member.link"
|
||||
hover:text-app-black
|
||||
class="text-app-black/50 text-16px hidden"
|
||||
lg="block"
|
||||
>Link
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</ProjectCreateComponentsItem>
|
||||
<ProjectCreateComponentsItemAdd
|
||||
button-label="ADD MEMBER"
|
||||
@add="addMember()"
|
||||
>
|
||||
<template #content>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
lg="gap-24px"
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newMember.name"
|
||||
lg="w-50%"
|
||||
label="Team member name"
|
||||
placeholder="Enter member name"
|
||||
hint="Full member's name"
|
||||
/>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="newMember.link"
|
||||
lg="w-50%"
|
||||
label="Profile URL"
|
||||
placeholder="Linkedin, Farcaster, Twitter,..."
|
||||
hint="Link to member’s social profile (ex. Twitter, Linkedin, Lens,...)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</ProjectCreateComponentsItemAdd>
|
||||
</div>
|
||||
</template>
|
107
components/Project/Create/Categories/Technology.vue
Normal file
107
components/Project/Create/Categories/Technology.vue
Normal file
|
@ -0,0 +1,107 @@
|
|||
<script setup lang="ts">
|
||||
import type { Project } from '~/types'
|
||||
|
||||
const props = defineProps<{
|
||||
project?: Partial<Project>
|
||||
}>()
|
||||
|
||||
const version = ref(props.project?.project_phase?.toLowerCase() || undefined)
|
||||
const openSource = ref(props.project?.blockchain_features?.opensource || false)
|
||||
const upgradability = ref(props.project?.blockchain_features?.upgradability?.enabled || false)
|
||||
const assetType = ref(props.project?.blockchain_features?.asset_custody_type?.toLowerCase() || '')
|
||||
const encryption = ref(props.project?.blockchain_features?.encryption || '')
|
||||
const peerToPeer = ref(props.project?.blockchain_features?.p2p)
|
||||
const decentralizedStorage = ref(props.project?.storage?.decentralized)
|
||||
|
||||
const { useProject, projectPhaseData, assetCustodyData } = useData()
|
||||
const { saveProject } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
project_phase: version.value,
|
||||
blockchain_features: {
|
||||
opensource: openSource.value,
|
||||
upgradability: {
|
||||
enabled: upgradability.value,
|
||||
},
|
||||
asset_custody_type: assetType.value,
|
||||
encryption: encryption.value,
|
||||
p2p: peerToPeer.value,
|
||||
},
|
||||
storage: {
|
||||
decentralized: decentralizedStorage.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="VERSION"
|
||||
/>
|
||||
<ProjectCreateComponentsRadio
|
||||
v-model="version"
|
||||
:options="projectPhaseData.map(p => ({ label: p.name, value: p.id }))"
|
||||
/>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="FEATURES"
|
||||
/>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-24px
|
||||
>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="openSource"
|
||||
label="Open source"
|
||||
hint="Check when projects source code is openly available and editable"
|
||||
/>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="upgradability"
|
||||
label="Upgradability"
|
||||
hint="Are developers able to upgrade and change deployed contract?"
|
||||
/>
|
||||
</div>
|
||||
<ProjectCreateComponentsSelect
|
||||
v-model="assetType"
|
||||
:options="assetCustodyData.map(c => ({ label: c.name, value: c.id }))"
|
||||
label="Asset custody type"
|
||||
placeholder="Select custody type"
|
||||
hint="How are user’s fund handled? (non-custody, multisig, pool,...)"
|
||||
/>
|
||||
<ProjectCreateComponentsCategoryDivider
|
||||
w-full
|
||||
title="ADDITIONAL INFO"
|
||||
/>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-24px
|
||||
>
|
||||
<ProjectCreateComponentsInput
|
||||
v-model="encryption"
|
||||
lg="w-1/2"
|
||||
label="Technology type / Encryption"
|
||||
hint="Define technologies your project uses for privacy ex. Zero-Knowledge (ZK), SHA256,"
|
||||
placeholder="Technology name"
|
||||
/>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="peerToPeer"
|
||||
label="Peer to Peer (P2P)"
|
||||
hint="Check when you transfer / communicate withou intermediaries"
|
||||
/>
|
||||
<ProjectCreateComponentsToggle
|
||||
v-model="decentralizedStorage"
|
||||
label="Decentralized storage"
|
||||
hint="Is your data hosted on IPFS, Filecoin or other decentralized storage?"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
38
components/Project/Create/Components/CategoryDivider.vue
Normal file
38
components/Project/Create/Components/CategoryDivider.vue
Normal file
|
@ -0,0 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
title: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
w-full
|
||||
flex
|
||||
justify-between
|
||||
gap-8px
|
||||
my-40px
|
||||
lg="gap-32px"
|
||||
>
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-8px
|
||||
text-16px
|
||||
font-700
|
||||
class="text-app-white/50"
|
||||
>
|
||||
<span text-nowrap>{{ title }}</span>
|
||||
</div>
|
||||
<div
|
||||
w-full
|
||||
flex
|
||||
items-center
|
||||
>
|
||||
<hr
|
||||
border="t-2px white/50"
|
||||
w-full
|
||||
shrink-1
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
119
components/Project/Create/Components/DatePicker.vue
Normal file
119
components/Project/Create/Components/DatePicker.vue
Normal file
|
@ -0,0 +1,119 @@
|
|||
<script setup lang="ts">
|
||||
export interface SelectProps {
|
||||
label?: string
|
||||
hint?: string
|
||||
placeholder?: string
|
||||
required?: boolean
|
||||
modelValue?: any
|
||||
}
|
||||
|
||||
defineProps<SelectProps>()
|
||||
|
||||
const months = [
|
||||
{ label: 'January', value: 1 },
|
||||
{ label: 'February', value: 2 },
|
||||
{ label: 'March', value: 3 },
|
||||
{ label: 'April', value: 4 },
|
||||
{ label: 'May', value: 5 },
|
||||
{ label: 'June', value: 6 },
|
||||
{ label: 'July', value: 7 },
|
||||
{ label: 'August', value: 8 },
|
||||
{ label: 'September', value: 9 },
|
||||
{ label: 'October', value: 10 },
|
||||
{ label: 'November', value: 11 },
|
||||
{ label: 'December', value: 12 },
|
||||
]
|
||||
const years = Array.from({ length: 101 }, (_, i) => new Date().getFullYear() - i)
|
||||
|
||||
const day = defineModel<number>('day')
|
||||
const month = defineModel<number>('month')
|
||||
const year = defineModel<number>('year')
|
||||
|
||||
const daysInMonth = (month: number, year: number) => {
|
||||
const monthObj = months.find(m => m.value === month)
|
||||
if (!monthObj) {
|
||||
throw new Error(`Invalid month value: ${month}`)
|
||||
}
|
||||
const monthIndex = months.indexOf(monthObj)
|
||||
return new Date(year, monthIndex + 1, 0).getDate()
|
||||
}
|
||||
|
||||
const days = computed(() => {
|
||||
if (month.value && year.value) {
|
||||
return Array.from({ length: daysInMonth(month.value, year.value) }, (_, i) => i + 1)
|
||||
}
|
||||
return Array.from({ length: 31 }, (_, i) => i + 1)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
>
|
||||
<label
|
||||
v-if="label"
|
||||
font-400
|
||||
text-14px
|
||||
lg:text-16px
|
||||
text-app-white
|
||||
>
|
||||
{{ label }}
|
||||
<span
|
||||
v-if="required"
|
||||
text-app-danger
|
||||
text-16px
|
||||
>*</span>
|
||||
</label>
|
||||
<div
|
||||
class="flex flex-col"
|
||||
gap-8px
|
||||
lg="flex flex-row gap-24px"
|
||||
>
|
||||
<div
|
||||
class="flex flex-row"
|
||||
lg="w-1/2"
|
||||
relative
|
||||
>
|
||||
<SelectBox
|
||||
v-model="day"
|
||||
w-full
|
||||
:options="days.map(day => ({ label: day.toString(), value: day }))"
|
||||
placeholder="Day"
|
||||
:border-opacity="30"
|
||||
:is-margin-top="false"
|
||||
/>
|
||||
<SelectBox
|
||||
v-model="month"
|
||||
w-full
|
||||
mx--2px
|
||||
:options="months.map(month => ({ label: month.label, value: month.value }))"
|
||||
placeholder="Month"
|
||||
:border-opacity="30"
|
||||
:is-margin-top="false"
|
||||
/>
|
||||
<SelectBox
|
||||
v-model="year"
|
||||
w-full
|
||||
:options="years.map(year => ({ label: year.toString(), value: year }))"
|
||||
placeholder="Year"
|
||||
:border-opacity="30"
|
||||
:is-margin-top="false"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
v-if="hint"
|
||||
lg="left-1/2 self-center"
|
||||
font-400
|
||||
italic
|
||||
lg:text-14px
|
||||
text-12px
|
||||
text-app-white
|
||||
opacity-50
|
||||
>
|
||||
{{ hint }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
81
components/Project/Create/Components/Input.vue
Normal file
81
components/Project/Create/Components/Input.vue
Normal file
|
@ -0,0 +1,81 @@
|
|||
<script setup lang="ts">
|
||||
export interface SelectProps {
|
||||
label?: string
|
||||
hint?: string
|
||||
placeholder?: string
|
||||
required?: boolean
|
||||
modelValue?: any
|
||||
textarea?: boolean
|
||||
textareaRows?: number
|
||||
lgWidth?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
defineProps<SelectProps>()
|
||||
|
||||
const model = defineModel<string>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="w-full!"
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
>
|
||||
<label
|
||||
v-if="label"
|
||||
font-400
|
||||
text-14px
|
||||
lg:text-16px
|
||||
text-app-white
|
||||
>
|
||||
{{ label }}
|
||||
<span
|
||||
v-if="required"
|
||||
text-app-danger
|
||||
text-16px
|
||||
>*</span>
|
||||
</label>
|
||||
<div
|
||||
w-full
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
lg="flex flex-row gap-24px"
|
||||
relative
|
||||
>
|
||||
<div v-bind="$attrs">
|
||||
<textarea
|
||||
v-if="textarea"
|
||||
v-model="model"
|
||||
:rows="textareaRows"
|
||||
:placeholder="placeholder"
|
||||
class="relative w-full p-8px text-left border-2px text-app-white bg-black sm:text-sm sm:leading-6 focus:ring-0 focus:outline-none"
|
||||
:class="error ? 'border-app-danger/50' : 'border-white/30'"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
v-model="model"
|
||||
:placeholder="placeholder"
|
||||
type="text"
|
||||
class="relative w-full p-8px text-left border-2px text-app-white bg-black sm:text-sm sm:leading-6 focus:ring-0 focus:outline-none"
|
||||
:class="error ? 'border-app-danger/50' : 'border-white/30'"
|
||||
>
|
||||
</div>
|
||||
<span
|
||||
v-if="hint"
|
||||
lg="left-1/2 self-center"
|
||||
v-bind="$attrs"
|
||||
font-400
|
||||
italic
|
||||
lg:text-14px
|
||||
text-12px
|
||||
text-app-white
|
||||
opacity-50
|
||||
>
|
||||
{{ hint }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
33
components/Project/Create/Components/Item.vue
Normal file
33
components/Project/Create/Components/Item.vue
Normal file
|
@ -0,0 +1,33 @@
|
|||
<script setup lang="ts">
|
||||
const emits = defineEmits(['remove'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex
|
||||
justify-between
|
||||
items-center
|
||||
py-12px
|
||||
px-16px
|
||||
bg-app-white
|
||||
>
|
||||
<slot name="label" />
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-16px
|
||||
>
|
||||
<slot
|
||||
class="text-app-black/50"
|
||||
name="desc"
|
||||
/>
|
||||
<button @click="emits('remove')">
|
||||
<UnoIcon
|
||||
i-heroicons-solid-x
|
||||
text-12px
|
||||
text-app-black
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
31
components/Project/Create/Components/ItemAdd.vue
Normal file
31
components/Project/Create/Components/ItemAdd.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
buttonLabel: string
|
||||
}>()
|
||||
|
||||
const emits = defineEmits(['add'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
w-full
|
||||
border-2
|
||||
border-app-white
|
||||
p-24px
|
||||
>
|
||||
<slot name="content" />
|
||||
<Button
|
||||
mt-16px
|
||||
lg="w-fit mt-24px"
|
||||
border
|
||||
inverted-color
|
||||
@click="emits('add')"
|
||||
>
|
||||
<span
|
||||
px-24px
|
||||
>{{ buttonLabel }}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
61
components/Project/Create/Components/Radio.vue
Normal file
61
components/Project/Create/Components/Radio.vue
Normal file
|
@ -0,0 +1,61 @@
|
|||
<script setup lang="ts">
|
||||
interface OptionItem<T> {
|
||||
label: string
|
||||
value: T
|
||||
}
|
||||
|
||||
export interface SelectProps {
|
||||
options: OptionItem<string>[]
|
||||
}
|
||||
|
||||
defineProps<SelectProps>()
|
||||
|
||||
const selected = defineModel<string>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div w-full>
|
||||
<div w-full>
|
||||
<HeadlessRadioGroup v-model="selected">
|
||||
<div class="flex flex-wrap gap-20px">
|
||||
<HeadlessRadioGroupOption
|
||||
v-for="option in options"
|
||||
:key="option.label"
|
||||
v-slot="{ checked }"
|
||||
as="template"
|
||||
:value="option.value"
|
||||
>
|
||||
<div class="cursor-pointer w-49% flex items-center gap-22px">
|
||||
<div
|
||||
rounded-full
|
||||
p-6px
|
||||
flex
|
||||
items-center
|
||||
justify-centerě
|
||||
:class="checked ? 'bg-app-white text-app-black' : 'outline outline-2px outline-offset--2 outline-app-white text-app-white'"
|
||||
>
|
||||
<UnoIcon
|
||||
v-if="checked"
|
||||
i-heroicons-solid-check
|
||||
text-20px
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
w-20px
|
||||
h-20px
|
||||
/>
|
||||
</div>
|
||||
<HeadlessRadioGroupLabel
|
||||
as="p"
|
||||
:class="checked ? 'text-app-white' : 'text-app-white/50'"
|
||||
class="font-medium"
|
||||
>
|
||||
{{ option.label }}
|
||||
</HeadlessRadioGroupLabel>
|
||||
</div>
|
||||
</HeadlessRadioGroupOption>
|
||||
</div>
|
||||
</HeadlessRadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
107
components/Project/Create/Components/Select.vue
Normal file
107
components/Project/Create/Components/Select.vue
Normal file
|
@ -0,0 +1,107 @@
|
|||
<script setup lang="ts">
|
||||
import type { InputOption } from '~/types'
|
||||
|
||||
const props = defineProps<SelectProps>()
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
interface SelectProps {
|
||||
options: InputOption[]
|
||||
label?: string
|
||||
modelValue: string
|
||||
placeholder?: string
|
||||
required?: boolean
|
||||
hint?: string
|
||||
}
|
||||
|
||||
const selectedValue = useVModel(props, 'modelValue', emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
lg="flex flex-row gap-24px"
|
||||
relative
|
||||
>
|
||||
<div lg="w-1/2">
|
||||
<HeadlessListbox
|
||||
v-model="selectedValue"
|
||||
as="div"
|
||||
>
|
||||
<HeadlessListboxLabel
|
||||
v-if="label"
|
||||
font-400
|
||||
text-14px
|
||||
lg:text-16px
|
||||
text-app-white
|
||||
>
|
||||
{{ label }}
|
||||
<span
|
||||
v-if="required"
|
||||
text-app-danger
|
||||
text-16px
|
||||
>*</span>
|
||||
</HeadlessListboxLabel>
|
||||
<div
|
||||
class="relative font-700 mt-2 bg-app-black"
|
||||
>
|
||||
<HeadlessListboxButton
|
||||
as="div"
|
||||
class="relative w-full cursor-pointer p-8px text-left border-2px text-app-white bg-black border-white/30 sm:text-sm sm:leading-6"
|
||||
>
|
||||
<span
|
||||
class="block truncate mr-8px"
|
||||
:class="[selectedValue ? 'text-app-white' : 'font-400 text-white/50']"
|
||||
>
|
||||
{{ props.options.find(option => option.value === selectedValue)?.label || props.placeholder }}
|
||||
</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<UnoIcon
|
||||
i-heroicons-solid-chevron-down
|
||||
class="text-app-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</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 border-white/30"
|
||||
>
|
||||
<HeadlessListboxOption
|
||||
v-for="option in props.options"
|
||||
:key="option.value"
|
||||
v-slot="{ selected }"
|
||||
as="template"
|
||||
:value="option.value"
|
||||
class="w-full relative cursor-pointer select-none py-8px p-16px border-white/30"
|
||||
>
|
||||
<span
|
||||
class="block truncate"
|
||||
:class="[selected ? 'font-semibold' : 'font-normal']"
|
||||
>{{ option.label }}</span>
|
||||
</HeadlessListboxOption>
|
||||
</HeadlessListboxOptions>
|
||||
</transition>
|
||||
</div>
|
||||
</HeadlessListbox>
|
||||
</div>
|
||||
<span
|
||||
v-if="hint"
|
||||
lg="mt-28px left-1/2 self-center"
|
||||
font-400
|
||||
italic
|
||||
lg:text-14px
|
||||
text-12px
|
||||
text-app-white
|
||||
opacity-50
|
||||
>
|
||||
{{ hint }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
213
components/Project/Create/Components/SelectChips.vue
Normal file
213
components/Project/Create/Components/SelectChips.vue
Normal file
|
@ -0,0 +1,213 @@
|
|||
<script setup lang="ts">
|
||||
interface OptionItem<T> {
|
||||
label: string
|
||||
value: T
|
||||
}
|
||||
|
||||
export interface SelectProps {
|
||||
options: OptionItem<any>[]
|
||||
label?: string
|
||||
hint?: string
|
||||
placeholder?: string
|
||||
required?: boolean
|
||||
multiple?: boolean
|
||||
canAddNew?: boolean
|
||||
modelValue?: any
|
||||
error?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<SelectProps>(), {
|
||||
multiple: true,
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
const modelValue = useVModel(props, 'modelValue', emits)
|
||||
|
||||
const query = ref('')
|
||||
const newOptions = ref<OptionItem<any>[]>([])
|
||||
|
||||
const options = computed(() => {
|
||||
return [...props.options, ...newOptions.value]
|
||||
})
|
||||
|
||||
const filteredOptions = computed(() =>
|
||||
query.value === ''
|
||||
? options.value
|
||||
: options.value.filter(o => o.label.toLowerCase().includes(query.value.toLowerCase())),
|
||||
)
|
||||
|
||||
// const modelValue = ref(props.modelValue || (props.multiple ? [] : null))
|
||||
|
||||
const selectedOptions = computed(() => props.multiple ? options.value.filter(o => modelValue.value?.includes(o.value)) : [])
|
||||
|
||||
function deleteOption(value: string) {
|
||||
const index = modelValue.value.indexOf(value)
|
||||
if (index !== -1)
|
||||
modelValue.value.splice(index, 1)
|
||||
|
||||
newOptions.value = newOptions.value.filter((o: any) => o.value !== value)
|
||||
}
|
||||
|
||||
function addOption() {
|
||||
if (query.value === '' || !props.canAddNew) return
|
||||
|
||||
if (!options.value.some(o => o.value === query.value)) {
|
||||
newOptions.value.push({ label: query.value, value: query.value })
|
||||
}
|
||||
|
||||
if (props.multiple) {
|
||||
if (!modelValue.value) modelValue.value = []
|
||||
modelValue.value.push(query.value)
|
||||
}
|
||||
else {
|
||||
modelValue.value = query.value
|
||||
}
|
||||
|
||||
query.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
lg="flex flex-row gap-24px"
|
||||
relative
|
||||
>
|
||||
<div lg="w-1/2">
|
||||
<HeadlessCombobox
|
||||
v-model="modelValue"
|
||||
:multiple="props.multiple"
|
||||
>
|
||||
<HeadlessComboboxLabel
|
||||
v-if="label"
|
||||
font-400
|
||||
text-14px
|
||||
lg:text-16px
|
||||
text-app-white
|
||||
>
|
||||
{{ label }}
|
||||
<span
|
||||
v-if="required"
|
||||
text-app-danger
|
||||
text-16px
|
||||
>*</span>
|
||||
</HeadlessComboboxLabel>
|
||||
<div class="relative font-700 mt-2 bg-app-black">
|
||||
<HeadlessComboboxButton
|
||||
as="div"
|
||||
class="relative w-full cursor-pointer p-8px text-left border-2px text-app-white bg-black sm:text-sm sm:leading-6"
|
||||
:class="error ? 'border-app-danger/50' : 'border-white/30'"
|
||||
>
|
||||
<div class="flex flex-wrap gap-8px">
|
||||
<span
|
||||
v-for="option in selectedOptions"
|
||||
:key="option.value"
|
||||
class="font-700 text-14px leading-20px flex gap-8px items-center bg-app-white text-black px-8px py-4px"
|
||||
lg="text-16px"
|
||||
>
|
||||
{{ option.label }}
|
||||
<button
|
||||
type="button"
|
||||
class="ml-1 text-black"
|
||||
@click.stop="deleteOption(option.value)"
|
||||
>
|
||||
<UnoIcon
|
||||
i-heroicons-solid-x
|
||||
text-16px
|
||||
text-black
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
<HeadlessComboboxInput
|
||||
class="text-14px font-400 leading-20px ml-8px w-fit bg-transparent border-none focus:ring-0 focus:outline-none"
|
||||
lg="16px"
|
||||
:placeholder="placeholder"
|
||||
@keyup.enter="addOption"
|
||||
@change="query = $event.target.value"
|
||||
/>
|
||||
</div>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2">
|
||||
<UnoIcon
|
||||
i-heroicons-solid-chevron-down
|
||||
class="text-app-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</HeadlessComboboxButton>
|
||||
</div>
|
||||
<Transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
@after-leave="query = ''"
|
||||
>
|
||||
<HeadlessComboboxOptions
|
||||
lg="w-1/2"
|
||||
class="w-full absolute z-100 max-h-60 divide-y-2px border-2px border-t-0 overflow-auto bg-app-black text-app-white focus:outline-none sm:text-sm border-white/30"
|
||||
>
|
||||
<div
|
||||
v-if="filteredOptions.length === 0 && query !== '' && props.canAddNew"
|
||||
class="gap-4px flex items-center relative cursor-default select-none px-4 py-2 text-app-white"
|
||||
@click="addOption"
|
||||
>
|
||||
<UnoIcon
|
||||
i-heroicons-solid-plus
|
||||
class="text-app-white"
|
||||
/>
|
||||
<span>Add</span>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="filteredOptions.length === 0 && query !== '' && !props.canAddNew"
|
||||
class="relative cursor-default select-none px-4 py-2 text-app-white opacity-50"
|
||||
>
|
||||
Nothing found.
|
||||
</div>
|
||||
<HeadlessComboboxOption
|
||||
v-for="option in filteredOptions"
|
||||
:key="option.value"
|
||||
v-slot="{ selected, active }"
|
||||
class="border-white/30"
|
||||
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>
|
||||
<span
|
||||
v-if="selected"
|
||||
class="absolute inset-y-0 right-0 flex items-center pr-3 text-white"
|
||||
>
|
||||
<UnoIcon
|
||||
i-heroicons-solid-check
|
||||
text-20px
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</li>
|
||||
</HeadlessComboboxOption>
|
||||
</HeadlessComboboxOptions>
|
||||
</Transition>
|
||||
</HeadlessCombobox>
|
||||
</div>
|
||||
<span
|
||||
v-if="hint"
|
||||
lg="mt-28px left-1/2 self-center"
|
||||
font-400
|
||||
italic
|
||||
lg:text-14px
|
||||
text-12px
|
||||
text-app-white
|
||||
opacity-50
|
||||
>
|
||||
{{ hint }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
81
components/Project/Create/Components/Toggle.vue
Normal file
81
components/Project/Create/Components/Toggle.vue
Normal file
|
@ -0,0 +1,81 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
label?: string
|
||||
hint?: string
|
||||
}>()
|
||||
|
||||
const enabled = defineModel<boolean>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
lg="flex flex-row gap-24px"
|
||||
relative
|
||||
>
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-24px
|
||||
lg="w-1/2"
|
||||
>
|
||||
<HeadlessSwitch
|
||||
v-model="enabled"
|
||||
:class="enabled ? 'bg-white/10' : 'bg-white/10'"
|
||||
class="relative inline-flex h-[28px] w-[68px] shrink-0 cursor-pointer transition-colors duration-200 ease-in-out focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
:class="enabled ? 'translate-x-9 bg-app-white' : 'translate-x-0 bg-app-black border-2 border-app-white'"
|
||||
flex
|
||||
items-center
|
||||
class="pointer-events-none inline-block h-[28px] w-[32px] transform shadow-lg ring-0 transition duration-200 ease-in-out"
|
||||
>
|
||||
<UnoIcon
|
||||
v-if="enabled"
|
||||
transition
|
||||
duration-200
|
||||
ease-in-out
|
||||
i-heroicons-solid-check
|
||||
w-full
|
||||
text-20px
|
||||
text-app-black
|
||||
/>
|
||||
<UnoIcon
|
||||
v-else
|
||||
transition
|
||||
duration-200
|
||||
ease-in-out
|
||||
i-heroicons-solid-x
|
||||
w-full
|
||||
text-20px
|
||||
text-app-white
|
||||
/>
|
||||
</span>
|
||||
</HeadlessSwitch>
|
||||
<span
|
||||
v-if="label"
|
||||
text-14px
|
||||
font-400
|
||||
lg="text-16px"
|
||||
:class="enabled ? 'text-app-white' : 'text-app-white/50'"
|
||||
>
|
||||
{{ label }}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
v-if="hint"
|
||||
lg="left-1/2 self-center"
|
||||
font-400
|
||||
italic
|
||||
lg:text-14px
|
||||
text-12px
|
||||
text-app-white
|
||||
opacity-50
|
||||
>
|
||||
{{ hint }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
|
@ -41,8 +41,6 @@ const calculateScore = computed(() => {
|
|||
else
|
||||
value = indexableProject?.[criterias[i].value]
|
||||
|
||||
// console.log(props.project?.links?.github);
|
||||
// console.log(Object.keys(props.indexableProject["team"]).length);
|
||||
if (value === null || value === undefined)
|
||||
continue
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ import { useRoute } from 'vue-router'
|
|||
|
||||
const route = useRoute()
|
||||
|
||||
const githubProjectUrl = computed(() => {
|
||||
return `https://github.com/web3privacy/explorer-data/blob/main/src/projects/${route.params.id}/index.yaml`
|
||||
})
|
||||
// const githubProjectUrl = computed(() => {
|
||||
// return `https://github.com/web3privacy/explorer-data/blob/main/src/projects/${route.params.id}/index.yaml`
|
||||
// })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -20,7 +20,7 @@ const githubProjectUrl = computed(() => {
|
|||
>
|
||||
<NavigationButton
|
||||
w-230px
|
||||
@click="$router.back()"
|
||||
@click="$router.push('/')"
|
||||
>
|
||||
<span
|
||||
block
|
||||
|
@ -46,9 +46,8 @@ const githubProjectUrl = computed(() => {
|
|||
<EditButton
|
||||
px-16px
|
||||
py-8px
|
||||
hover:bg-white
|
||||
hover:text-black
|
||||
:to="githubProjectUrl"
|
||||
hover="cursor-pointer bg-white text-black"
|
||||
@click="$router.push('/project/' + route.params.id + '/edit')"
|
||||
>
|
||||
<span
|
||||
text-16px
|
||||
|
|
|
@ -4,14 +4,18 @@ import type { InputOption } from '~/types'
|
|||
const props = withDefaults(defineProps<SelectProps>(), {
|
||||
isMarginTop: true,
|
||||
blackAndWhite: true,
|
||||
borderOpacity: 100,
|
||||
})
|
||||
const emits = defineEmits(['update:modelValue'])
|
||||
|
||||
interface SelectProps {
|
||||
options: InputOption[]
|
||||
modelValue: string
|
||||
label?: string
|
||||
modelValue: any
|
||||
isMarginTop?: boolean
|
||||
blackAndWhite?: boolean
|
||||
borderOpacity?: number
|
||||
placeholder?: string
|
||||
}
|
||||
|
||||
const selectedValue = useVModel(props, 'modelValue', emits)
|
||||
|
@ -28,13 +32,18 @@ const selectedValue = useVModel(props, 'modelValue', emits)
|
|||
>
|
||||
<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']"
|
||||
:class="[blackAndWhite ? 'text-app-white' : 'text-app-black', `border-white/${borderOpacity}`]"
|
||||
>
|
||||
<span class="block truncate mr-8px">{{ props.options.find(option => option.value === selectedValue)?.label }}</span>
|
||||
<span
|
||||
class="block truncate mr-8px"
|
||||
:class="[selectedValue ? 'text-app-white' : 'font-400 text-white/50']"
|
||||
>
|
||||
{{ props.options.find(option => option.value === selectedValue)?.label || props.placeholder }}
|
||||
</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']"
|
||||
:class="[blackAndWhite ? 'text-app-white' : 'text-app-black']"
|
||||
/>
|
||||
</span>
|
||||
</HeadlessListboxButton>
|
||||
|
@ -45,24 +54,22 @@ const selectedValue = useVModel(props, 'modelValue', emits)
|
|||
leave-to-class="opacity-0"
|
||||
>
|
||||
<HeadlessListboxOptions
|
||||
:class="`border-white/${borderOpacity}`"
|
||||
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 }"
|
||||
v-slot="{ 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']"
|
||||
class="py-8px p-16px cursor-pointer"
|
||||
:class="`border-white/${borderOpacity}`"
|
||||
>
|
||||
<span
|
||||
class="block truncate"
|
||||
:class="[selected ? 'font-semibold' : 'font-normal']"
|
||||
>{{ option.label }}</span>
|
||||
</li>
|
||||
</HeadlessListboxOption>
|
||||
</HeadlessListboxOptions>
|
||||
</transition>
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
import { Buffer } from 'buffer'
|
||||
import type { Category, Project, ProjectShallow } from '~/types'
|
||||
|
||||
export const useData = defineStore('data', () => {
|
||||
const assetsData = useState<{ id: string, name: string }[]>('assetsData')
|
||||
const categoriesData = useState<{ id: string, name: string }[]>('categoriesData')
|
||||
const featuresData = useState<{ id: string, name: string }[]>('featuresData')
|
||||
const ecosystemsData = useState<{ id: string, name: string }[]>('ecosystemsData')
|
||||
const usecasesData = useState<{ id: string, name: string }[]>('usecasesData')
|
||||
const projectPhaseData = useState<{ id: string, name: string }[]>('projectPhaseData')
|
||||
const assetCustodyData = useState<{ id: string, name: string }[]>('assetCustodyData')
|
||||
const signInRequirmentsData = useState<{ id: string, name: string }[]>('signInRequirmenetsData')
|
||||
|
||||
const categories = useState<Category[]>('categories')
|
||||
const projects = useState<Project[]>('projects')
|
||||
const selectedCategoryId = useState(() => 'defi')
|
||||
|
@ -25,6 +35,13 @@ export const useData = defineStore('data', () => {
|
|||
const data = await $fetch<{
|
||||
categories: Category[]
|
||||
projects: Project[]
|
||||
assets: { id: string, name: string }[]
|
||||
features: { id: string, name: string }[]
|
||||
ecosystems: { id: string, name: string }[]
|
||||
usecases: { id: string, name: string }[]
|
||||
project_phase: { id: string, name: string }[]
|
||||
asset_custody_type: { id: string, name: string }[]
|
||||
sign_in_type_requirments: { id: string, name: string }[]
|
||||
}>('/api/data')
|
||||
projects.value = data.projects.filter(p => p.name)
|
||||
categories.value = data.categories.map((c) => {
|
||||
|
@ -33,6 +50,15 @@ export const useData = defineStore('data', () => {
|
|||
).length
|
||||
return c
|
||||
}).filter(c => c.projectsCount > 0)
|
||||
|
||||
assetsData.value = data.assets.map(a => ({ id: a.id.toLowerCase(), name: a.name }))
|
||||
categoriesData.value = data.categories.map(c => ({ id: c.id.toLowerCase(), name: c.name }))
|
||||
featuresData.value = data.features.map(f => ({ id: f.id.toLowerCase(), name: f.name }))
|
||||
ecosystemsData.value = data.ecosystems.map(e => ({ id: e.id.toLowerCase(), name: e.name }))
|
||||
usecasesData.value = data.usecases.map(u => ({ id: u.id.toLowerCase(), name: u.name }))
|
||||
projectPhaseData.value = data.project_phase.map(p => ({ id: p.id.toLowerCase(), name: p.name }))
|
||||
assetCustodyData.value = data.asset_custody_type.map(a => ({ id: a.id.toLowerCase(), name: a.name }))
|
||||
signInRequirmentsData.value = data.sign_in_type_requirments.map(s => ({ id: s.id.toLowerCase(), name: s.name }))
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
|
@ -120,11 +146,74 @@ export const useData = defineStore('data', () => {
|
|||
|
||||
const filteredProjectsCount = computed(() => filteredProjects.value.length)
|
||||
|
||||
function useProject() {
|
||||
const project = ref<Partial<Project>>()
|
||||
const projectImage = ref<File>()
|
||||
|
||||
function setProject(id: string) {
|
||||
project.value = getProjectById(id, { shallow: false }) as Project
|
||||
}
|
||||
|
||||
function clearProject() {
|
||||
project.value = undefined
|
||||
}
|
||||
|
||||
function saveProject(data: Partial<Project>) {
|
||||
project.value = {
|
||||
...project.value,
|
||||
...data,
|
||||
}
|
||||
}
|
||||
|
||||
function saveProjectImage(image: File) {
|
||||
projectImage.value = image
|
||||
}
|
||||
|
||||
async function publishProject() {
|
||||
try {
|
||||
const imageArrayBuffer = await projectImage.value?.arrayBuffer()
|
||||
let imageBuffer: Buffer | undefined
|
||||
if (imageArrayBuffer)
|
||||
imageBuffer = Buffer.from(imageArrayBuffer)
|
||||
await $fetch(`/api/data`, {
|
||||
method: 'POST',
|
||||
body: {
|
||||
project: project.value,
|
||||
image: {
|
||||
type: projectImage.value?.type,
|
||||
data: imageBuffer?.toString('base64'),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
project,
|
||||
setProject,
|
||||
clearProject,
|
||||
saveProject,
|
||||
saveProjectImage,
|
||||
publishProject,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
selectedCategoryId,
|
||||
filter,
|
||||
switcher,
|
||||
categories,
|
||||
assetsData,
|
||||
categoriesData,
|
||||
featuresData,
|
||||
ecosystemsData,
|
||||
usecasesData,
|
||||
projectPhaseData,
|
||||
assetCustodyData,
|
||||
signInRequirmentsData,
|
||||
projects,
|
||||
shallowProjects,
|
||||
filteredProjectsCount,
|
||||
|
@ -133,5 +222,6 @@ export const useData = defineStore('data', () => {
|
|||
getProjectsByCategory,
|
||||
filteredProjects,
|
||||
projectToShallow,
|
||||
useProject,
|
||||
}
|
||||
})
|
||||
|
|
6
layouts/create.vue
Normal file
6
layouts/create.vue
Normal file
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<div h-full w-full>
|
||||
<Navigation />
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
|
@ -10,6 +10,7 @@ export default defineNuxtConfig({
|
|||
'nuxt-lodash',
|
||||
'nuxt-headlessui',
|
||||
'@nuxt/image',
|
||||
'@vee-validate/nuxt',
|
||||
],
|
||||
sourcemap: {
|
||||
server: true,
|
||||
|
|
|
@ -26,16 +26,20 @@
|
|||
"@nuxtjs/color-mode": "^3.4.4",
|
||||
"@pinia/nuxt": "^0.5.4",
|
||||
"@unocss/nuxt": "^0.62.3",
|
||||
"@vee-validate/nuxt": "^4.13.2",
|
||||
"@vueuse/nuxt": "^11.0.3",
|
||||
"eslint": "^9.9.1",
|
||||
"nuxt": "^3.13.0",
|
||||
"nuxt-headlessui": "^1.2.0",
|
||||
"nuxt-lodash": "^2.5.3",
|
||||
"octokit": "^4.0.2",
|
||||
"pinia": "^2.2.2",
|
||||
"taze": "^0.16.7",
|
||||
"typescript": "^5.5.4",
|
||||
"vitest": "^2.0.5",
|
||||
"vue-tsc": "^2.1.4"
|
||||
"vue-tsc": "^2.1.4",
|
||||
"yaml": "^2.5.1",
|
||||
"yup": "^1.4.0"
|
||||
},
|
||||
"overrides": {
|
||||
"vue": "latest"
|
||||
|
|
333
pages/project/[id]/edit.vue
Normal file
333
pages/project/[id]/edit.vue
Normal file
|
@ -0,0 +1,333 @@
|
|||
<script setup lang="ts">
|
||||
import ProjectCreateCategoriesBasicInfo from '~/components/Project/Create/Categories/BasicInfo.vue'
|
||||
import ProjectCreateCategoriesAssets from '~/components/Project/Create/Categories/Assets.vue'
|
||||
import ProjectCreateCategoriesLinks from '~/components/Project/Create/Categories/Links.vue'
|
||||
import ProjectCreateCategoriesTechnology from '~/components/Project/Create/Categories/Technology.vue'
|
||||
import ProjectCreateCategoriesPrivacy from '~/components/Project/Create/Categories/Privacy.vue'
|
||||
import ProjectCreateCategoriesSecurity from '~/components/Project/Create/Categories/Security.vue'
|
||||
import ProjectCreateCategoriesTeam from '~/components/Project/Create/Categories/Team.vue'
|
||||
import ProjectCreateCategoriesFunding from '~/components/Project/Create/Categories/Funding.vue'
|
||||
import ProjectCreateCategoriesHistory from '~/components/Project/Create/Categories/History.vue'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'create',
|
||||
})
|
||||
|
||||
const { useProject, projects } = useData()
|
||||
const { saveProject, setProject, project, publishProject, saveProjectImage } = useProject()
|
||||
|
||||
const route = useRoute()
|
||||
await until(projects).toMatch(p => p?.length > 0)
|
||||
setProject(route.params.id as string)
|
||||
|
||||
if (!project.value) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: 'Project not found',
|
||||
fatal: true,
|
||||
})
|
||||
}
|
||||
|
||||
const tabs = reactive([
|
||||
{ label: 'Basic Info', value: 'basic_info', component: ProjectCreateCategoriesBasicInfo },
|
||||
{ label: 'Assets', value: 'assets', component: ProjectCreateCategoriesAssets },
|
||||
{ label: 'Links', value: 'links', component: ProjectCreateCategoriesLinks },
|
||||
{ label: 'Technology', value: 'technology', component: ProjectCreateCategoriesTechnology },
|
||||
{ label: 'Privacy', value: 'privacy', component: ProjectCreateCategoriesPrivacy },
|
||||
{ label: 'Security', value: 'security', component: ProjectCreateCategoriesSecurity },
|
||||
{ label: 'Team', value: 'team', component: ProjectCreateCategoriesTeam },
|
||||
{ label: 'Funding', value: 'funding', component: ProjectCreateCategoriesFunding },
|
||||
{ label: 'History', value: 'history', component: ProjectCreateCategoriesHistory },
|
||||
])
|
||||
|
||||
const selectedTab = ref(tabs[0].value)
|
||||
|
||||
function getCurrentComponent() {
|
||||
const tab = tabs.find(t => t.value === selectedTab.value) || tabs[0]
|
||||
return tab.component || null
|
||||
}
|
||||
|
||||
const currentComponent = ref()
|
||||
|
||||
const { open, onChange } = useFileDialog({
|
||||
accept: 'image/*', // Set to accept only image files
|
||||
})
|
||||
|
||||
const logoSrc = ref(project.value?.logos?.[0].url || '/no-image-1-1.svg')
|
||||
|
||||
onChange((files) => {
|
||||
if (!files?.[0]) return
|
||||
const file = files[0]
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
logoSrc.value = e.target?.result as string
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
saveProjectImage(file)
|
||||
})
|
||||
|
||||
const projectNameInput = ref<HTMLInputElement | null>(null)
|
||||
function useProjectName() {
|
||||
const isEditing = ref(false)
|
||||
const name = ref('Untitled')
|
||||
|
||||
function toggleEdit() {
|
||||
isEditing.value = !isEditing.value
|
||||
if (isEditing.value) {
|
||||
setTimeout(() => {
|
||||
projectNameInput.value?.focus()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isEditing,
|
||||
name,
|
||||
toggleEdit,
|
||||
}
|
||||
}
|
||||
|
||||
const { isEditing, name, toggleEdit } = useProjectName()
|
||||
name.value = project.value?.name || 'Untitled'
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
name: name.value,
|
||||
})
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (selectedTab.value === 'basic_info') {
|
||||
if (!currentComponent.value.isFormValid())
|
||||
return
|
||||
else save()
|
||||
}
|
||||
|
||||
currentComponent.value.save()
|
||||
|
||||
const currentIndex = tabs.findIndex(tab => tab.value === selectedTab.value)
|
||||
const nextTab = tabs[currentIndex + 1] || tabs[0]
|
||||
selectedTab.value = nextTab.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div w-full>
|
||||
<div
|
||||
bg-app-bg-dark_grey
|
||||
px-16px
|
||||
py-24px
|
||||
lg="pb-0px mb--1px"
|
||||
>
|
||||
<div app-container>
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-16px
|
||||
>
|
||||
<div
|
||||
relative
|
||||
class="parent"
|
||||
>
|
||||
<NuxtImg
|
||||
lg="w-100px h-100px"
|
||||
w-64px
|
||||
h-64px
|
||||
bg-app-bg-grey
|
||||
object-cover
|
||||
border-2
|
||||
class="border-app-white/30"
|
||||
:src="logoSrc ?? '/no-image-1-1.svg'"
|
||||
/>
|
||||
<button
|
||||
h-24px
|
||||
hidden
|
||||
parent-hover:flex
|
||||
absolute
|
||||
bottom-0
|
||||
w-full
|
||||
border-t-0
|
||||
border-2
|
||||
border-black
|
||||
border-opacity-50
|
||||
h-fit
|
||||
justify-center
|
||||
text="12px"
|
||||
font-700
|
||||
bg-app-white
|
||||
text-app-black
|
||||
@click="open()"
|
||||
>
|
||||
Upload Logo
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
>
|
||||
<h3
|
||||
font-400
|
||||
text="14px app-white/50"
|
||||
leading-20px
|
||||
>
|
||||
{{ 'Project Name' }}
|
||||
</h3>
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-12px
|
||||
>
|
||||
<input
|
||||
v-if="isEditing"
|
||||
ref="projectNameInput"
|
||||
v-model="name"
|
||||
w-fit
|
||||
onkeydown="this.style.width = 0; this.style.width = this.scrollWidth + 2 + 'px';"
|
||||
type="text"
|
||||
font-700
|
||||
text-20px
|
||||
leading-28px
|
||||
bg-app-bg-dark_grey
|
||||
onfocus="this.style.width = 0; this.style.width = this.scrollWidth + 2 + 'px';"
|
||||
>
|
||||
<h2
|
||||
v-else
|
||||
font-700
|
||||
text-20px
|
||||
leading-28px
|
||||
>
|
||||
{{ name }}
|
||||
</h2>
|
||||
<button @click="toggleEdit()">
|
||||
<UnoIcon
|
||||
v-if="isEditing"
|
||||
text-24px
|
||||
class="text-app-white/30"
|
||||
hover:text-app-white
|
||||
i-heroicons-solid-check
|
||||
/>
|
||||
<UnoIcon
|
||||
v-else
|
||||
text-20px
|
||||
class="text-app-white/30"
|
||||
hover:text-app-white
|
||||
i-heroicons-solid-pencil
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
flex
|
||||
w-full
|
||||
gap-46px
|
||||
lg="mt-24px"
|
||||
>
|
||||
<SelectBox
|
||||
v-model="selectedTab"
|
||||
label="Choose category"
|
||||
:options="tabs.map(t => ({ label: t.label, value: t.value }))"
|
||||
:border-opacity="30"
|
||||
w-full
|
||||
lg:hidden
|
||||
block
|
||||
mt-16px
|
||||
/>
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
lg:block
|
||||
hidden
|
||||
pb-8px
|
||||
leading-40px
|
||||
:class="selectedTab === tab.value ? 'font-bold border-b-4 border-app-white' : ''"
|
||||
@click="selectedTab = tab.value"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
border-t-2
|
||||
class="border-app-white/30"
|
||||
px-16px
|
||||
py-24px
|
||||
>
|
||||
<div
|
||||
app-container
|
||||
mb-55px
|
||||
>
|
||||
<component
|
||||
:is="getCurrentComponent()"
|
||||
v-if="project"
|
||||
ref="currentComponent"
|
||||
:project="project"
|
||||
w-full
|
||||
flex
|
||||
flex-col
|
||||
gap-24px
|
||||
/>
|
||||
<Button
|
||||
v-if="selectedTab !== tabs[tabs.length - 1].value"
|
||||
class="hidden!"
|
||||
mt-48px
|
||||
lg="w-fit flex!"
|
||||
border
|
||||
@click="next()"
|
||||
>
|
||||
<span px-24px>NEXT SECTION</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
justify-center
|
||||
text-center
|
||||
absolute
|
||||
bottom-0
|
||||
w-full
|
||||
bg-app-bg-dark_grey
|
||||
class="border-app-white/30"
|
||||
lg="bg-app-black w-fit border-l-2 border-t-2 right-0 border-app-white"
|
||||
p-12px
|
||||
>
|
||||
<Button
|
||||
v-if="selectedTab !== tabs[tabs.length - 1].value"
|
||||
flex
|
||||
lg="w-fit hidden!"
|
||||
border
|
||||
@click="next()"
|
||||
>
|
||||
<span px-24px>NEXT SECTION</span>
|
||||
</Button>
|
||||
<span
|
||||
v-if="selectedTab !== tabs[tabs.length - 1].value"
|
||||
lg="hidden"
|
||||
block
|
||||
text="12px italic app-white/50"
|
||||
>or you can submit changes by publishing them</span>
|
||||
<div flex>
|
||||
<Button
|
||||
w-full
|
||||
lg="w-fit"
|
||||
border
|
||||
>
|
||||
<span px-24px>CANCEL</span>
|
||||
</Button>
|
||||
<Button
|
||||
w-full
|
||||
lg="w-fit"
|
||||
inverted-color
|
||||
@click="publishProject()"
|
||||
>
|
||||
<span px-24px>PUBLISH</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
319
pages/project/create.vue
Normal file
319
pages/project/create.vue
Normal file
|
@ -0,0 +1,319 @@
|
|||
<script setup lang="ts">
|
||||
import ProjectCreateCategoriesBasicInfo from '~/components/Project/Create/Categories/BasicInfo.vue'
|
||||
import ProjectCreateCategoriesAssets from '~/components/Project/Create/Categories/Assets.vue'
|
||||
import ProjectCreateCategoriesLinks from '~/components/Project/Create/Categories/Links.vue'
|
||||
import ProjectCreateCategoriesTechnology from '~/components/Project/Create/Categories/Technology.vue'
|
||||
import ProjectCreateCategoriesPrivacy from '~/components/Project/Create/Categories/Privacy.vue'
|
||||
import ProjectCreateCategoriesSecurity from '~/components/Project/Create/Categories/Security.vue'
|
||||
import ProjectCreateCategoriesTeam from '~/components/Project/Create/Categories/Team.vue'
|
||||
import ProjectCreateCategoriesFunding from '~/components/Project/Create/Categories/Funding.vue'
|
||||
import ProjectCreateCategoriesHistory from '~/components/Project/Create/Categories/History.vue'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'create',
|
||||
})
|
||||
|
||||
const tabs = reactive([
|
||||
{ label: 'Basic Info', value: 'basic_info', component: ProjectCreateCategoriesBasicInfo },
|
||||
{ label: 'Assets', value: 'assets', component: ProjectCreateCategoriesAssets },
|
||||
{ label: 'Links', value: 'links', component: ProjectCreateCategoriesLinks },
|
||||
{ label: 'Technology', value: 'technology', component: ProjectCreateCategoriesTechnology },
|
||||
{ label: 'Privacy', value: 'privacy', component: ProjectCreateCategoriesPrivacy },
|
||||
{ label: 'Security', value: 'security', component: ProjectCreateCategoriesSecurity },
|
||||
{ label: 'Team', value: 'team', component: ProjectCreateCategoriesTeam },
|
||||
{ label: 'Funding', value: 'funding', component: ProjectCreateCategoriesFunding },
|
||||
{ label: 'History', value: 'history', component: ProjectCreateCategoriesHistory },
|
||||
])
|
||||
|
||||
const selectedTab = ref(tabs[0].value)
|
||||
|
||||
function getCurrentComponent() {
|
||||
const tab = tabs.find(t => t.value === selectedTab.value) || tabs[0]
|
||||
return tab.component || null
|
||||
}
|
||||
|
||||
const currentComponent = ref()
|
||||
|
||||
const { open, onChange } = useFileDialog({
|
||||
accept: 'image/*', // Set to accept only image files
|
||||
})
|
||||
|
||||
const logoSrc = ref('/no-image-1-1.svg')
|
||||
|
||||
onChange((files) => {
|
||||
if (!files?.[0]) return
|
||||
const file = files[0]
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
logoSrc.value = e.target?.result as string
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
|
||||
saveProjectImage(file)
|
||||
})
|
||||
|
||||
const projectNameInput = ref<HTMLInputElement | null>(null)
|
||||
function useProjectName() {
|
||||
const isEditing = ref(false)
|
||||
const name = ref('Untitled')
|
||||
|
||||
function toggleEdit() {
|
||||
isEditing.value = !isEditing.value
|
||||
if (isEditing.value) {
|
||||
setTimeout(() => {
|
||||
projectNameInput.value?.focus()
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isEditing,
|
||||
name,
|
||||
toggleEdit,
|
||||
}
|
||||
}
|
||||
|
||||
const { isEditing, name, toggleEdit } = useProjectName()
|
||||
|
||||
const { useProject } = useData()
|
||||
const { saveProject, publishProject, saveProjectImage } = useProject()
|
||||
|
||||
function save() {
|
||||
saveProject({
|
||||
name: name.value,
|
||||
})
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (selectedTab.value === 'basic_info') {
|
||||
if (!currentComponent.value.isFormValid())
|
||||
return
|
||||
else save()
|
||||
}
|
||||
|
||||
currentComponent.value.save()
|
||||
|
||||
const currentIndex = tabs.findIndex(tab => tab.value === selectedTab.value)
|
||||
const nextTab = tabs[currentIndex + 1] || tabs[0]
|
||||
selectedTab.value = nextTab.value
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div w-full>
|
||||
<div
|
||||
bg-app-bg-dark_grey
|
||||
px-16px
|
||||
py-24px
|
||||
lg="pb-0px mb--1px"
|
||||
>
|
||||
<div app-container>
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-16px
|
||||
>
|
||||
<div
|
||||
relative
|
||||
class="parent"
|
||||
>
|
||||
<NuxtImg
|
||||
lg="w-100px h-100px"
|
||||
w-64px
|
||||
h-64px
|
||||
bg-app-bg-grey
|
||||
object-cover
|
||||
border-2
|
||||
class="border-app-white/30"
|
||||
opacity-30
|
||||
:src="logoSrc ?? '/no-image-1-1.svg'"
|
||||
/>
|
||||
<button
|
||||
h-24px
|
||||
hidden
|
||||
parent-hover:flex
|
||||
absolute
|
||||
bottom-0
|
||||
w-full
|
||||
border-t-0
|
||||
border-2
|
||||
border-black
|
||||
border-opacity-80
|
||||
h-fit
|
||||
justify-center
|
||||
text="12px"
|
||||
font-700
|
||||
bg-app-white
|
||||
text-app-black
|
||||
@click="open()"
|
||||
>
|
||||
Upload Logo
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-8px
|
||||
>
|
||||
<h3
|
||||
font-400
|
||||
text="14px app-white/50"
|
||||
leading-20px
|
||||
>
|
||||
{{ 'Project Name' }}
|
||||
</h3>
|
||||
<div
|
||||
flex
|
||||
items-center
|
||||
gap-12px
|
||||
>
|
||||
<input
|
||||
v-if="isEditing"
|
||||
ref="projectNameInput"
|
||||
v-model="name"
|
||||
w-fit
|
||||
onkeydown="this.style.width = 0; this.style.width = this.scrollWidth + 2 + 'px';"
|
||||
type="text"
|
||||
font-700
|
||||
text-20px
|
||||
leading-28px
|
||||
bg-app-bg-dark_grey
|
||||
onfocus="this.style.width = 0; this.style.width = this.scrollWidth + 2 + 'px';"
|
||||
>
|
||||
<h2
|
||||
v-else
|
||||
font-700
|
||||
text-20px
|
||||
leading-28px
|
||||
>
|
||||
{{ name }}
|
||||
</h2>
|
||||
<button @click="toggleEdit()">
|
||||
<UnoIcon
|
||||
v-if="isEditing"
|
||||
text-24px
|
||||
class="text-app-white/30"
|
||||
hover:text-app-white
|
||||
i-heroicons-solid-check
|
||||
/>
|
||||
<UnoIcon
|
||||
v-else
|
||||
text-20px
|
||||
class="text-app-white/30"
|
||||
hover:text-app-white
|
||||
i-heroicons-solid-pencil
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
flex
|
||||
w-full
|
||||
gap-46px
|
||||
lg="mt-24px"
|
||||
>
|
||||
<SelectBox
|
||||
v-model="selectedTab"
|
||||
label="Choose category"
|
||||
:options="tabs.map(t => ({ label: t.label, value: t.value }))"
|
||||
:border-opacity="30"
|
||||
w-full
|
||||
lg:hidden
|
||||
block
|
||||
mt-16px
|
||||
/>
|
||||
<button
|
||||
v-for="tab in tabs"
|
||||
:key="tab.value"
|
||||
lg:block
|
||||
hidden
|
||||
pb-8px
|
||||
leading-40px
|
||||
:class="selectedTab === tab.value ? 'font-bold border-b-4 border-app-white' : ''"
|
||||
@click="selectedTab = tab.value"
|
||||
>
|
||||
{{ tab.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
border-t-2
|
||||
class="border-app-white/30"
|
||||
px-16px
|
||||
py-24px
|
||||
>
|
||||
<div
|
||||
app-container
|
||||
mb-55px
|
||||
>
|
||||
<component
|
||||
:is="getCurrentComponent()"
|
||||
ref="currentComponent"
|
||||
w-full
|
||||
flex
|
||||
flex-col
|
||||
gap-24px
|
||||
/>
|
||||
<Button
|
||||
v-if="selectedTab !== tabs[tabs.length - 1].value"
|
||||
class="hidden!"
|
||||
mt-48px
|
||||
lg="w-fit flex!"
|
||||
border
|
||||
@click="next()"
|
||||
>
|
||||
<span px-24px>NEXT SECTION</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
flex
|
||||
flex-col
|
||||
gap-16px
|
||||
justify-center
|
||||
text-center
|
||||
absolute
|
||||
bottom-0
|
||||
w-full
|
||||
bg-app-bg-dark_grey
|
||||
class="border-app-white/30"
|
||||
lg="bg-app-black w-fit border-l-2 border-t-2 right-0 border-app-white"
|
||||
p-12px
|
||||
>
|
||||
<Button
|
||||
v-if="selectedTab !== tabs[tabs.length - 1].value"
|
||||
flex
|
||||
lg="w-fit hidden!"
|
||||
border
|
||||
@click="next()"
|
||||
>
|
||||
<span px-24px>NEXT SECTION</span>
|
||||
</Button>
|
||||
<span
|
||||
v-if="selectedTab !== tabs[tabs.length - 1].value"
|
||||
lg="hidden"
|
||||
block
|
||||
text="12px italic app-white/50"
|
||||
>or you can submit changes by publishing them</span>
|
||||
<div flex>
|
||||
<Button
|
||||
w-full
|
||||
lg="w-fit"
|
||||
border
|
||||
>
|
||||
<span px-24px>CANCEL</span>
|
||||
</Button>
|
||||
<Button
|
||||
w-full
|
||||
lg="w-fit"
|
||||
inverted-color
|
||||
@click="publishProject()"
|
||||
>
|
||||
<span px-24px>PUBLISH</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
16
plugins/githubApi.ts
Normal file
16
plugins/githubApi.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export default defineNuxtPlugin(() => {
|
||||
const appConfig = useAppConfig()
|
||||
|
||||
const githubApi = $fetch.create({
|
||||
baseURL: 'https://api.github.com',
|
||||
headers: {
|
||||
Authorization: `Bearer ${appConfig.github.accessToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
provide: {
|
||||
githubApi,
|
||||
},
|
||||
}
|
||||
})
|
452
pnpm-lock.yaml
452
pnpm-lock.yaml
|
@ -38,6 +38,9 @@ importers:
|
|||
'@unocss/nuxt':
|
||||
specifier: ^0.62.3
|
||||
version: 0.62.3(magicast@0.3.5)(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.2(@types/node@20.8.7)(terser@5.22.0))(webpack@5.89.0(esbuild@0.23.1))
|
||||
'@vee-validate/nuxt':
|
||||
specifier: ^4.13.2
|
||||
version: 4.13.2(magicast@0.3.5)(rollup@4.21.2)(vue@3.4.38(typescript@5.5.4))
|
||||
'@vueuse/nuxt':
|
||||
specifier: ^11.0.3
|
||||
version: 11.0.3(magicast@0.3.5)(nuxt@3.13.0(@parcel/watcher@2.4.1)(@types/node@20.8.7)(encoding@0.1.13)(eslint@9.9.1(jiti@1.21.6))(ioredis@5.4.1)(magicast@0.3.5)(optionator@0.9.3)(rollup@4.21.2)(terser@5.22.0)(typescript@5.5.4)(vite@5.4.2(@types/node@20.8.7)(terser@5.22.0))(vue-tsc@2.1.4(typescript@5.5.4)))(rollup@4.21.2)(vue@3.4.38(typescript@5.5.4))
|
||||
|
@ -53,6 +56,9 @@ importers:
|
|||
nuxt-lodash:
|
||||
specifier: ^2.5.3
|
||||
version: 2.5.3(rollup@4.21.2)
|
||||
octokit:
|
||||
specifier: ^4.0.2
|
||||
version: 4.0.2
|
||||
pinia:
|
||||
specifier: ^2.2.2
|
||||
version: 2.2.2(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))
|
||||
|
@ -68,6 +74,12 @@ importers:
|
|||
vue-tsc:
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4(typescript@5.5.4)
|
||||
yaml:
|
||||
specifier: ^2.5.1
|
||||
version: 2.5.1
|
||||
yup:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
|
||||
packages:
|
||||
|
||||
|
@ -1320,6 +1332,113 @@ packages:
|
|||
'@nuxtjs/color-mode@3.4.4':
|
||||
resolution: {integrity: sha512-VSNJVGnRIjiGmfbMa0cN+rwNRowDRTL/wku/z5MpKSanVo3khIRitBNqNviso1l3T+LW0pLHeXBNp6L8g/l1EA==}
|
||||
|
||||
'@octokit/app@15.1.0':
|
||||
resolution: {integrity: sha512-TkBr7QgOmE6ORxvIAhDbZsqPkF7RSqTY4pLTtUQCvr6dTXqvi2fFo46q3h1lxlk/sGMQjqyZ0kEahkD/NyzOHg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/auth-app@7.1.1':
|
||||
resolution: {integrity: sha512-kRAd6yelV9OgvlEJE88H0VLlQdZcag9UlLr7dV0YYP37X8PPDvhgiTy66QVhDXdyoT0AleFN2w/qXkPdrSzINg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/auth-oauth-app@8.1.1':
|
||||
resolution: {integrity: sha512-5UtmxXAvU2wfcHIPPDWzVSAWXVJzG3NWsxb7zCFplCWEmMCArSZV0UQu5jw5goLQXbFyOr5onzEH37UJB3zQQg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/auth-oauth-device@7.1.1':
|
||||
resolution: {integrity: sha512-HWl8lYueHonuyjrKKIup/1tiy0xcmQCdq5ikvMO1YwkNNkxb6DXfrPjrMYItNLyCP/o2H87WuijuE+SlBTT8eg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/auth-oauth-user@5.1.1':
|
||||
resolution: {integrity: sha512-rRkMz0ErOppdvEfnemHJXgZ9vTPhBuC6yASeFaB7I2yLMd7QpjfrL1mnvRPlyKo+M6eeLxrKanXJ9Qte29SRsw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/auth-token@5.1.1':
|
||||
resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/auth-unauthenticated@6.1.0':
|
||||
resolution: {integrity: sha512-zPSmfrUAcspZH/lOFQnVnvjQZsIvmfApQH6GzJrkIunDooU1Su2qt2FfMTSVPRp7WLTQyC20Kd55lF+mIYaohQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/core@6.1.2':
|
||||
resolution: {integrity: sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/endpoint@10.1.1':
|
||||
resolution: {integrity: sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/graphql@8.1.1':
|
||||
resolution: {integrity: sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/oauth-app@7.1.3':
|
||||
resolution: {integrity: sha512-EHXbOpBkSGVVGF1W+NLMmsnSsJRkcrnVmDKt0TQYRBb6xWfWzoi9sBD4DIqZ8jGhOWO/V8t4fqFyJ4vDQDn9bg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/oauth-authorization-url@7.1.1':
|
||||
resolution: {integrity: sha512-ooXV8GBSabSWyhLUowlMIVd9l1s2nsOGQdlP2SQ4LnkEsGXzeCvbSbCPdZThXhEFzleGPwbapT0Sb+YhXRyjCA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/oauth-methods@5.1.2':
|
||||
resolution: {integrity: sha512-C5lglRD+sBlbrhCUTxgJAFjWgJlmTx5bQ7Ch0+2uqRjYv7Cfb5xpX4WuSC9UgQna3sqRGBL9EImX9PvTpMaQ7g==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/openapi-types@22.2.0':
|
||||
resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==}
|
||||
|
||||
'@octokit/openapi-webhooks-types@8.3.0':
|
||||
resolution: {integrity: sha512-vKLsoR4xQxg4Z+6rU/F65ItTUz/EXbD+j/d4mlq2GW8TsA4Tc8Kdma2JTAAJ5hrKWUQzkR/Esn2fjsqiVRYaQg==}
|
||||
|
||||
'@octokit/plugin-paginate-graphql@5.2.2':
|
||||
resolution: {integrity: sha512-7znSVvlNAOJisCqAnjN1FtEziweOHSjPGAuc5W58NeGNAr/ZB57yCsjQbXDlWsVryA7hHQaEQPcBbJYFawlkyg==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=6'
|
||||
|
||||
'@octokit/plugin-paginate-rest@11.3.3':
|
||||
resolution: {integrity: sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=6'
|
||||
|
||||
'@octokit/plugin-rest-endpoint-methods@13.2.4':
|
||||
resolution: {integrity: sha512-gusyAVgTrPiuXOdfqOySMDztQHv6928PQ3E4dqVGEtOvRXAKRbJR4b1zQyniIT9waqaWk/UDaoJ2dyPr7Bk7Iw==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=6'
|
||||
|
||||
'@octokit/plugin-retry@7.1.1':
|
||||
resolution: {integrity: sha512-G9Ue+x2odcb8E1XIPhaFBnTTIrrUDfXN05iFXiqhR+SeeeDMMILcAnysOsxUpEWcQp2e5Ft397FCXTcPkiPkLw==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': '>=6'
|
||||
|
||||
'@octokit/plugin-throttling@9.3.1':
|
||||
resolution: {integrity: sha512-Qd91H4liUBhwLB2h6jZ99bsxoQdhgPk6TdwnClPyTBSDAdviGPceViEgUwj+pcQDmB/rfAXAXK7MTochpHM3yQ==}
|
||||
engines: {node: '>= 18'}
|
||||
peerDependencies:
|
||||
'@octokit/core': ^6.0.0
|
||||
|
||||
'@octokit/request-error@6.1.4':
|
||||
resolution: {integrity: sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/request@9.1.3':
|
||||
resolution: {integrity: sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/types@13.5.0':
|
||||
resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==}
|
||||
|
||||
'@octokit/webhooks-methods@5.1.0':
|
||||
resolution: {integrity: sha512-yFZa3UH11VIxYnnoOYCVoJ3q4ChuSOk2IVBBQ0O3xtKX4x9bmKb/1t+Mxixv2iUhzMdOl1qeWJqEhouXXzB3rQ==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@octokit/webhooks@13.3.0':
|
||||
resolution: {integrity: sha512-TUkJLtI163Bz5+JK0O+zDkQpn4gKwN+BovclUvCj6pI/6RXrFqQvUMRS2M+Rt8Rv0qR3wjoMoOPmpJKeOh0nBg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.4.1':
|
||||
resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
@ -1668,12 +1787,12 @@ packages:
|
|||
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
'@types/aws-lambda@8.10.145':
|
||||
resolution: {integrity: sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw==}
|
||||
|
||||
'@types/eslint-scope@3.7.6':
|
||||
resolution: {integrity: sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==}
|
||||
|
||||
'@types/eslint@8.44.6':
|
||||
resolution: {integrity: sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw==}
|
||||
|
||||
'@types/eslint@9.6.1':
|
||||
resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==}
|
||||
|
||||
|
@ -1878,6 +1997,9 @@ packages:
|
|||
peerDependencies:
|
||||
webpack: ^4 || ^5
|
||||
|
||||
'@vee-validate/nuxt@4.13.2':
|
||||
resolution: {integrity: sha512-rpYYO2isCrgHRdJnKRXGPn8aqhyZvCPUJgNubftPum1txbngkO/LNiFsZdFDO7auGprDAQf7axpszRfeKoCGVg==}
|
||||
|
||||
'@vercel/nft@0.26.5':
|
||||
resolution: {integrity: sha512-NHxohEqad6Ra/r4lGknO52uc/GrWILXAMs1BB4401GTqww0fw1bAqzpG1XHuDO+dprg4GvsD9ZLLSsdo78p9hQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
@ -2235,6 +2357,9 @@ packages:
|
|||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
before-after-hook@3.0.2:
|
||||
resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==}
|
||||
|
||||
binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -2251,6 +2376,9 @@ packages:
|
|||
boolbase@1.0.0:
|
||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||
|
||||
bottleneck@2.19.5:
|
||||
resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==}
|
||||
|
||||
brace-expansion@1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
|
||||
|
@ -2821,9 +2949,6 @@ packages:
|
|||
errx@0.1.0:
|
||||
resolution: {integrity: sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==}
|
||||
|
||||
es-module-lexer@1.3.1:
|
||||
resolution: {integrity: sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==}
|
||||
|
||||
es-module-lexer@1.5.4:
|
||||
resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==}
|
||||
|
||||
|
@ -3560,9 +3685,6 @@ packages:
|
|||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
|
||||
jsonc-parser@3.2.0:
|
||||
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
|
||||
|
||||
jsonfile@6.1.0:
|
||||
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
|
||||
|
||||
|
@ -3821,9 +3943,6 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
mlly@1.4.2:
|
||||
resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
|
||||
|
||||
mlly@1.7.1:
|
||||
resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
|
||||
|
||||
|
@ -3994,6 +4113,10 @@ packages:
|
|||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
octokit@4.0.2:
|
||||
resolution: {integrity: sha512-wbqF4uc1YbcldtiBFfkSnquHtECEIpYD78YUXI6ri1Im5OO2NLo6ZVpRdbJpdnpZ05zMrVPssNiEo6JQtea+Qg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
ofetch@1.3.3:
|
||||
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
|
||||
|
||||
|
@ -4168,9 +4291,6 @@ packages:
|
|||
typescript:
|
||||
optional: true
|
||||
|
||||
pkg-types@1.0.3:
|
||||
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
|
||||
|
||||
pkg-types@1.2.0:
|
||||
resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==}
|
||||
|
||||
|
@ -4394,6 +4514,9 @@ packages:
|
|||
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
property-expr@2.0.6:
|
||||
resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==}
|
||||
|
||||
protocols@2.0.1:
|
||||
resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==}
|
||||
|
||||
|
@ -4883,6 +5006,9 @@ packages:
|
|||
text-table@0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
|
||||
|
||||
tiny-case@1.0.3:
|
||||
resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==}
|
||||
|
||||
tiny-invariant@1.3.1:
|
||||
resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==}
|
||||
|
||||
|
@ -4920,6 +5046,9 @@ packages:
|
|||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
toposort@2.0.2:
|
||||
resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==}
|
||||
|
||||
totalist@3.0.1:
|
||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -4964,10 +5093,18 @@ packages:
|
|||
resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
type-fest@2.19.0:
|
||||
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
type-fest@3.13.1:
|
||||
resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
type-fest@4.26.1:
|
||||
resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
typescript@5.5.4:
|
||||
resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
|
||||
engines: {node: '>=14.17'}
|
||||
|
@ -5022,6 +5159,12 @@ packages:
|
|||
resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
|
||||
universal-github-app-jwt@2.2.0:
|
||||
resolution: {integrity: sha512-G5o6f95b5BggDGuUfKDApKaCgNYy2x7OdHY0zSMF081O0EJobw+1130VONhrA7ezGSV2FNOGyM+KQpQZAr9bIQ==}
|
||||
|
||||
universal-user-agent@7.0.2:
|
||||
resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==}
|
||||
|
||||
universalify@2.0.0:
|
||||
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
@ -5184,6 +5327,11 @@ packages:
|
|||
resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
|
||||
vee-validate@4.13.2:
|
||||
resolution: {integrity: sha512-HlpR/6MJ92TW9f135umMZKUqdd/tFQTxLNSf2ImbU4Y/MlLVAUpF1l64VdjTOhbClAqPjCb5p/SqHDxLpUHXrw==}
|
||||
peerDependencies:
|
||||
vue: ^3.4.26
|
||||
|
||||
vite-hot-client@0.2.3:
|
||||
resolution: {integrity: sha512-rOGAV7rUlUHX89fP2p2v0A2WWvV3QMX2UYq0fRqsWSvFvev4atHWqjwGoKaZT1VTKyLGk533ecu3eyd0o59CAg==}
|
||||
peerDependencies:
|
||||
|
@ -5486,8 +5634,8 @@ packages:
|
|||
yallist@4.0.0:
|
||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||
|
||||
yaml@2.5.0:
|
||||
resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==}
|
||||
yaml@2.5.1:
|
||||
resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==}
|
||||
engines: {node: '>= 14'}
|
||||
hasBin: true
|
||||
|
||||
|
@ -5507,6 +5655,9 @@ packages:
|
|||
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
yup@1.4.0:
|
||||
resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==}
|
||||
|
||||
zhead@2.2.4:
|
||||
resolution: {integrity: sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==}
|
||||
|
||||
|
@ -6771,9 +6922,9 @@ snapshots:
|
|||
ignore: 5.2.4
|
||||
jiti: 1.20.0
|
||||
knitwork: 1.0.0
|
||||
mlly: 1.4.2
|
||||
mlly: 1.7.1
|
||||
pathe: 1.1.1
|
||||
pkg-types: 1.0.3
|
||||
pkg-types: 1.2.0
|
||||
scule: 1.0.0
|
||||
semver: 7.5.4
|
||||
ufo: 1.3.1
|
||||
|
@ -6809,7 +6960,7 @@ snapshots:
|
|||
defu: 6.1.2
|
||||
hookable: 5.5.3
|
||||
pathe: 1.1.1
|
||||
pkg-types: 1.0.3
|
||||
pkg-types: 1.2.0
|
||||
postcss-import-resolver: 2.0.0
|
||||
std-env: 3.4.3
|
||||
ufo: 1.3.1
|
||||
|
@ -6952,6 +7103,152 @@ snapshots:
|
|||
- rollup
|
||||
- supports-color
|
||||
|
||||
'@octokit/app@15.1.0':
|
||||
dependencies:
|
||||
'@octokit/auth-app': 7.1.1
|
||||
'@octokit/auth-unauthenticated': 6.1.0
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/oauth-app': 7.1.3
|
||||
'@octokit/plugin-paginate-rest': 11.3.3(@octokit/core@6.1.2)
|
||||
'@octokit/types': 13.5.0
|
||||
'@octokit/webhooks': 13.3.0
|
||||
|
||||
'@octokit/auth-app@7.1.1':
|
||||
dependencies:
|
||||
'@octokit/auth-oauth-app': 8.1.1
|
||||
'@octokit/auth-oauth-user': 5.1.1
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
lru-cache: 10.4.3
|
||||
universal-github-app-jwt: 2.2.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/auth-oauth-app@8.1.1':
|
||||
dependencies:
|
||||
'@octokit/auth-oauth-device': 7.1.1
|
||||
'@octokit/auth-oauth-user': 5.1.1
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/types': 13.5.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/auth-oauth-device@7.1.1':
|
||||
dependencies:
|
||||
'@octokit/oauth-methods': 5.1.2
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/types': 13.5.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/auth-oauth-user@5.1.1':
|
||||
dependencies:
|
||||
'@octokit/auth-oauth-device': 7.1.1
|
||||
'@octokit/oauth-methods': 5.1.2
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/types': 13.5.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/auth-token@5.1.1': {}
|
||||
|
||||
'@octokit/auth-unauthenticated@6.1.0':
|
||||
dependencies:
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
|
||||
'@octokit/core@6.1.2':
|
||||
dependencies:
|
||||
'@octokit/auth-token': 5.1.1
|
||||
'@octokit/graphql': 8.1.1
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
before-after-hook: 3.0.2
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/endpoint@10.1.1':
|
||||
dependencies:
|
||||
'@octokit/types': 13.5.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/graphql@8.1.1':
|
||||
dependencies:
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/types': 13.5.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/oauth-app@7.1.3':
|
||||
dependencies:
|
||||
'@octokit/auth-oauth-app': 8.1.1
|
||||
'@octokit/auth-oauth-user': 5.1.1
|
||||
'@octokit/auth-unauthenticated': 6.1.0
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/oauth-authorization-url': 7.1.1
|
||||
'@octokit/oauth-methods': 5.1.2
|
||||
'@types/aws-lambda': 8.10.145
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/oauth-authorization-url@7.1.1': {}
|
||||
|
||||
'@octokit/oauth-methods@5.1.2':
|
||||
dependencies:
|
||||
'@octokit/oauth-authorization-url': 7.1.1
|
||||
'@octokit/request': 9.1.3
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
|
||||
'@octokit/openapi-types@22.2.0': {}
|
||||
|
||||
'@octokit/openapi-webhooks-types@8.3.0': {}
|
||||
|
||||
'@octokit/plugin-paginate-graphql@5.2.2(@octokit/core@6.1.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 6.1.2
|
||||
|
||||
'@octokit/plugin-paginate-rest@11.3.3(@octokit/core@6.1.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/types': 13.5.0
|
||||
|
||||
'@octokit/plugin-rest-endpoint-methods@13.2.4(@octokit/core@6.1.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/types': 13.5.0
|
||||
|
||||
'@octokit/plugin-retry@7.1.1(@octokit/core@6.1.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
bottleneck: 2.19.5
|
||||
|
||||
'@octokit/plugin-throttling@9.3.1(@octokit/core@6.1.2)':
|
||||
dependencies:
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/types': 13.5.0
|
||||
bottleneck: 2.19.5
|
||||
|
||||
'@octokit/request-error@6.1.4':
|
||||
dependencies:
|
||||
'@octokit/types': 13.5.0
|
||||
|
||||
'@octokit/request@9.1.3':
|
||||
dependencies:
|
||||
'@octokit/endpoint': 10.1.1
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
universal-user-agent: 7.0.2
|
||||
|
||||
'@octokit/types@13.5.0':
|
||||
dependencies:
|
||||
'@octokit/openapi-types': 22.2.0
|
||||
|
||||
'@octokit/webhooks-methods@5.1.0': {}
|
||||
|
||||
'@octokit/webhooks@13.3.0':
|
||||
dependencies:
|
||||
'@octokit/openapi-webhooks-types': 8.3.0
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/webhooks-methods': 5.1.0
|
||||
|
||||
'@parcel/watcher-android-arm64@2.4.1':
|
||||
optional: true
|
||||
|
||||
|
@ -7221,15 +7518,12 @@ snapshots:
|
|||
|
||||
'@trysound/sax@0.2.0': {}
|
||||
|
||||
'@types/aws-lambda@8.10.145': {}
|
||||
|
||||
'@types/eslint-scope@3.7.6':
|
||||
dependencies:
|
||||
'@types/eslint': 8.44.6
|
||||
'@types/estree': 1.0.3
|
||||
|
||||
'@types/eslint@8.44.6':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.3
|
||||
'@types/json-schema': 7.0.14
|
||||
'@types/eslint': 9.6.1
|
||||
'@types/estree': 1.0.5
|
||||
|
||||
'@types/eslint@9.6.1':
|
||||
dependencies:
|
||||
|
@ -7572,6 +7866,17 @@ snapshots:
|
|||
- rollup
|
||||
- supports-color
|
||||
|
||||
'@vee-validate/nuxt@4.13.2(magicast@0.3.5)(rollup@4.21.2)(vue@3.4.38(typescript@5.5.4))':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.13.0(magicast@0.3.5)(rollup@4.21.2)
|
||||
local-pkg: 0.5.0
|
||||
vee-validate: 4.13.2(vue@3.4.38(typescript@5.5.4))
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
- rollup
|
||||
- supports-color
|
||||
- vue
|
||||
|
||||
'@vercel/nft@0.26.5(encoding@0.1.13)':
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.11(encoding@0.1.13)
|
||||
|
@ -7947,9 +8252,9 @@ snapshots:
|
|||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
|
||||
acorn-import-assertions@1.9.0(acorn@8.11.2):
|
||||
acorn-import-assertions@1.9.0(acorn@8.12.1):
|
||||
dependencies:
|
||||
acorn: 8.11.2
|
||||
acorn: 8.12.1
|
||||
|
||||
acorn-import-attributes@1.9.5(acorn@8.12.1):
|
||||
dependencies:
|
||||
|
@ -8085,6 +8390,8 @@ snapshots:
|
|||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
before-after-hook@3.0.2: {}
|
||||
|
||||
binary-extensions@2.2.0: {}
|
||||
|
||||
bindings@1.5.0:
|
||||
|
@ -8102,6 +8409,8 @@ snapshots:
|
|||
|
||||
boolbase@1.0.0: {}
|
||||
|
||||
bottleneck@2.19.5: {}
|
||||
|
||||
brace-expansion@1.1.11:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
@ -8195,11 +8504,11 @@ snapshots:
|
|||
dotenv: 16.3.1
|
||||
giget: 1.1.3
|
||||
jiti: 1.20.0
|
||||
mlly: 1.4.2
|
||||
mlly: 1.7.1
|
||||
ohash: 1.1.3
|
||||
pathe: 1.1.1
|
||||
perfect-debounce: 1.0.0
|
||||
pkg-types: 1.0.3
|
||||
pkg-types: 1.2.0
|
||||
rc9: 2.1.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -8662,8 +8971,6 @@ snapshots:
|
|||
|
||||
errx@0.1.0: {}
|
||||
|
||||
es-module-lexer@1.3.1: {}
|
||||
|
||||
es-module-lexer@1.5.4: {}
|
||||
|
||||
esbuild@0.19.10:
|
||||
|
@ -9595,8 +9902,6 @@ snapshots:
|
|||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jsonc-parser@3.2.0: {}
|
||||
|
||||
jsonfile@6.1.0:
|
||||
dependencies:
|
||||
universalify: 2.0.0
|
||||
|
@ -9668,7 +9973,7 @@ snapshots:
|
|||
|
||||
local-pkg@0.5.0:
|
||||
dependencies:
|
||||
mlly: 1.4.2
|
||||
mlly: 1.7.1
|
||||
pkg-types: 1.2.0
|
||||
|
||||
locate-path@5.0.0:
|
||||
|
@ -9851,13 +10156,6 @@ snapshots:
|
|||
|
||||
mkdirp@1.0.4: {}
|
||||
|
||||
mlly@1.4.2:
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
pathe: 1.1.1
|
||||
pkg-types: 1.0.3
|
||||
ufo: 1.3.1
|
||||
|
||||
mlly@1.7.1:
|
||||
dependencies:
|
||||
acorn: 8.12.1
|
||||
|
@ -10205,6 +10503,19 @@ snapshots:
|
|||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
octokit@4.0.2:
|
||||
dependencies:
|
||||
'@octokit/app': 15.1.0
|
||||
'@octokit/core': 6.1.2
|
||||
'@octokit/oauth-app': 7.1.3
|
||||
'@octokit/plugin-paginate-graphql': 5.2.2(@octokit/core@6.1.2)
|
||||
'@octokit/plugin-paginate-rest': 11.3.3(@octokit/core@6.1.2)
|
||||
'@octokit/plugin-rest-endpoint-methods': 13.2.4(@octokit/core@6.1.2)
|
||||
'@octokit/plugin-retry': 7.1.1(@octokit/core@6.1.2)
|
||||
'@octokit/plugin-throttling': 9.3.1(@octokit/core@6.1.2)
|
||||
'@octokit/request-error': 6.1.4
|
||||
'@octokit/types': 13.5.0
|
||||
|
||||
ofetch@1.3.3:
|
||||
dependencies:
|
||||
destr: 2.0.2
|
||||
|
@ -10373,12 +10684,6 @@ snapshots:
|
|||
optionalDependencies:
|
||||
typescript: 5.5.4
|
||||
|
||||
pkg-types@1.0.3:
|
||||
dependencies:
|
||||
jsonc-parser: 3.2.0
|
||||
mlly: 1.4.2
|
||||
pathe: 1.1.1
|
||||
|
||||
pkg-types@1.2.0:
|
||||
dependencies:
|
||||
confbox: 0.1.7
|
||||
|
@ -10597,6 +10902,8 @@ snapshots:
|
|||
kleur: 3.0.3
|
||||
sisteransi: 1.0.5
|
||||
|
||||
property-expr@2.0.6: {}
|
||||
|
||||
protocols@2.0.1: {}
|
||||
|
||||
prr@1.0.1: {}
|
||||
|
@ -10793,7 +11100,7 @@ snapshots:
|
|||
|
||||
schema-utils@3.3.0:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.14
|
||||
'@types/json-schema': 7.0.15
|
||||
ajv: 6.12.6
|
||||
ajv-keywords: 3.5.2(ajv@6.12.6)
|
||||
|
||||
|
@ -11141,7 +11448,7 @@ snapshots:
|
|||
|
||||
terser-webpack-plugin@5.3.9(esbuild@0.23.1)(webpack@5.89.0(esbuild@0.23.1)):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.20
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
jest-worker: 27.5.1
|
||||
schema-utils: 3.3.0
|
||||
serialize-javascript: 6.0.1
|
||||
|
@ -11159,6 +11466,8 @@ snapshots:
|
|||
|
||||
text-table@0.2.0: {}
|
||||
|
||||
tiny-case@1.0.3: {}
|
||||
|
||||
tiny-invariant@1.3.1: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
@ -11184,6 +11493,8 @@ snapshots:
|
|||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
toposort@2.0.2: {}
|
||||
|
||||
totalist@3.0.1: {}
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
@ -11218,8 +11529,12 @@ snapshots:
|
|||
|
||||
type-fest@0.8.1: {}
|
||||
|
||||
type-fest@2.19.0: {}
|
||||
|
||||
type-fest@3.13.1: {}
|
||||
|
||||
type-fest@4.26.1: {}
|
||||
|
||||
typescript@5.5.4: {}
|
||||
|
||||
ufo@1.3.1: {}
|
||||
|
@ -11293,9 +11608,9 @@ snapshots:
|
|||
fast-glob: 3.3.1
|
||||
local-pkg: 0.4.3
|
||||
magic-string: 0.30.5
|
||||
mlly: 1.4.2
|
||||
mlly: 1.7.1
|
||||
pathe: 1.1.1
|
||||
pkg-types: 1.0.3
|
||||
pkg-types: 1.2.0
|
||||
scule: 1.0.0
|
||||
strip-literal: 1.3.0
|
||||
unplugin: 1.5.0
|
||||
|
@ -11310,6 +11625,10 @@ snapshots:
|
|||
dependencies:
|
||||
imurmurhash: 0.1.4
|
||||
|
||||
universal-github-app-jwt@2.2.0: {}
|
||||
|
||||
universal-user-agent@7.0.2: {}
|
||||
|
||||
universalify@2.0.0: {}
|
||||
|
||||
unocss@0.62.3(@unocss/webpack@0.62.3(rollup@4.21.2)(webpack@5.89.0(esbuild@0.23.1)))(postcss@8.4.44)(rollup@4.21.2)(vite@5.4.2(@types/node@20.8.7)(terser@5.22.0)):
|
||||
|
@ -11357,7 +11676,7 @@ snapshots:
|
|||
pathe: 1.1.2
|
||||
scule: 1.3.0
|
||||
unplugin: 1.12.3
|
||||
yaml: 2.5.0
|
||||
yaml: 2.5.1
|
||||
optionalDependencies:
|
||||
vue-router: 4.4.3(vue@3.4.38(typescript@5.5.4))
|
||||
transitivePeerDependencies:
|
||||
|
@ -11488,6 +11807,12 @@ snapshots:
|
|||
dependencies:
|
||||
builtins: 5.0.1
|
||||
|
||||
vee-validate@4.13.2(vue@3.4.38(typescript@5.5.4)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 6.6.3
|
||||
type-fest: 4.26.1
|
||||
vue: 3.4.38(typescript@5.5.4)
|
||||
|
||||
vite-hot-client@0.2.3(vite@5.4.2(@types/node@20.8.7)(terser@5.22.0)):
|
||||
dependencies:
|
||||
vite: 5.4.2(@types/node@20.8.7)(terser@5.22.0)
|
||||
|
@ -11724,16 +12049,16 @@ snapshots:
|
|||
webpack@5.89.0(esbuild@0.23.1):
|
||||
dependencies:
|
||||
'@types/eslint-scope': 3.7.6
|
||||
'@types/estree': 1.0.3
|
||||
'@types/estree': 1.0.5
|
||||
'@webassemblyjs/ast': 1.11.6
|
||||
'@webassemblyjs/wasm-edit': 1.11.6
|
||||
'@webassemblyjs/wasm-parser': 1.11.6
|
||||
acorn: 8.11.2
|
||||
acorn-import-assertions: 1.9.0(acorn@8.11.2)
|
||||
browserslist: 4.22.2
|
||||
acorn: 8.12.1
|
||||
acorn-import-assertions: 1.9.0(acorn@8.12.1)
|
||||
browserslist: 4.23.3
|
||||
chrome-trace-event: 1.0.3
|
||||
enhanced-resolve: 5.15.0
|
||||
es-module-lexer: 1.3.1
|
||||
es-module-lexer: 1.5.4
|
||||
eslint-scope: 5.1.1
|
||||
events: 3.3.0
|
||||
glob-to-regexp: 0.4.1
|
||||
|
@ -11804,7 +12129,7 @@ snapshots:
|
|||
|
||||
yallist@4.0.0: {}
|
||||
|
||||
yaml@2.5.0: {}
|
||||
yaml@2.5.1: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
|
@ -11822,6 +12147,13 @@ snapshots:
|
|||
|
||||
yocto-queue@1.0.0: {}
|
||||
|
||||
yup@1.4.0:
|
||||
dependencies:
|
||||
property-expr: 2.0.6
|
||||
tiny-case: 1.0.3
|
||||
toposort: 2.0.2
|
||||
type-fest: 2.19.0
|
||||
|
||||
zhead@2.2.4: {}
|
||||
|
||||
zip-stream@6.0.1:
|
||||
|
|
127
server/api/data.post.ts
Normal file
127
server/api/data.post.ts
Normal file
|
@ -0,0 +1,127 @@
|
|||
import { App } from 'octokit'
|
||||
import yaml from 'yaml'
|
||||
import type { Project } from '~/types'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<{ project: Project, image?: { type: string, data: string } }>(event)
|
||||
const yamlProject = yaml.stringify(body.project)
|
||||
const { appId, privateKey, installationId } = useAppConfig().github
|
||||
|
||||
const id = body.project.id || body.project.name.toLowerCase().replace(/\s+/g, '-')
|
||||
|
||||
const app = new App({
|
||||
appId,
|
||||
privateKey,
|
||||
})
|
||||
await app.octokit.rest.apps.getAuthenticated()
|
||||
const octokit = await app.getInstallationOctokit(installationId)
|
||||
|
||||
const owner = 'develit-io'
|
||||
const repo = 'test-repo'
|
||||
const baseBranch = 'main'
|
||||
const newBranchName = `${id}-project-update`
|
||||
const commitMessage = `${body.project.id ? `Updating the project: ${body.project.name}` : `Initiating the creation of project: ${body.project.name}`}`
|
||||
|
||||
const files = [
|
||||
{
|
||||
path: `src/projects/${id}/index.yaml`,
|
||||
content: yamlProject,
|
||||
encoding: 'utf-8',
|
||||
},
|
||||
]
|
||||
|
||||
if (body.image?.data && body.image?.type) {
|
||||
files.push(
|
||||
{
|
||||
path: `src/projects/${id}/logo.${body.image.type.split('/')[1]}`,
|
||||
content: body.image.data,
|
||||
encoding: 'base64',
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async function createBranch(owner: string, repo: string, newBranchName: string, baseBranch: string) {
|
||||
const { data: baseBranchData } = await octokit.rest.git.getRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${baseBranch}`,
|
||||
})
|
||||
|
||||
await octokit.rest.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/heads/${newBranchName}`,
|
||||
sha: baseBranchData.object.sha,
|
||||
})
|
||||
}
|
||||
|
||||
async function commitChangesToNewBranch(owner: string, repo: string, newBranch: string, message: string, files: { path: string, content: string }[]) {
|
||||
const { data: latestCommit } = await octokit.rest.repos.getCommit({
|
||||
owner,
|
||||
repo,
|
||||
ref: newBranch,
|
||||
})
|
||||
|
||||
const { data: baseTree } = await octokit.rest.git.getTree({
|
||||
owner,
|
||||
repo,
|
||||
tree_sha: latestCommit.commit.tree.sha,
|
||||
})
|
||||
|
||||
const tree = files.map(file => ({
|
||||
path: file.path,
|
||||
mode: '100644' as const,
|
||||
type: 'blob' as const,
|
||||
content: file.content,
|
||||
}))
|
||||
|
||||
const { data: newTree } = await octokit.rest.git.createTree({
|
||||
owner,
|
||||
repo,
|
||||
base_tree: baseTree.sha,
|
||||
tree,
|
||||
})
|
||||
|
||||
const { data: newCommit } = await octokit.rest.git.createCommit({
|
||||
owner,
|
||||
repo,
|
||||
message,
|
||||
tree: newTree.sha,
|
||||
parents: [latestCommit.sha],
|
||||
})
|
||||
|
||||
await octokit.rest.git.updateRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `heads/${newBranch}`,
|
||||
sha: newCommit.sha,
|
||||
})
|
||||
}
|
||||
|
||||
async function createPullRequest(owner: string, repo: string, head: string, base: string, title: string, body: string) {
|
||||
const { data: pullRequest } = await octokit.rest.pulls.create({
|
||||
owner,
|
||||
repo,
|
||||
title,
|
||||
head,
|
||||
base,
|
||||
body,
|
||||
})
|
||||
|
||||
return pullRequest
|
||||
}
|
||||
|
||||
try {
|
||||
await createBranch(owner, repo, newBranchName, baseBranch)
|
||||
console.log(`Branch ${newBranchName} created successfully!`)
|
||||
|
||||
await commitChangesToNewBranch(owner, repo, newBranchName, commitMessage, files)
|
||||
console.log(`Changes committed to branch ${newBranchName} successfully!`)
|
||||
|
||||
const pullRequestData = await createPullRequest(owner, repo, newBranchName, baseBranch, `${body.project.id ? `Update project: ${body.project.name}` : `Create project: ${body.project.name}`}`, `${body.project.id ? `Updating the project: ${body.project.name}` : `Initiating the creation of project: ${body.project.name}`}`)
|
||||
console.log('Pull request created:', pullRequestData)
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error during GitHub operations:', error)
|
||||
}
|
||||
})
|
|
@ -16185,5 +16185,47 @@
|
|||
"id": "other",
|
||||
"name": "Other"
|
||||
}
|
||||
],
|
||||
"project_phase": [
|
||||
{
|
||||
"id": "mainnet",
|
||||
"name": "Mainnet"
|
||||
},
|
||||
{
|
||||
"id": "testnet",
|
||||
"name": "Testnet"
|
||||
},
|
||||
{
|
||||
"id": "alpha",
|
||||
"name": "Alpha"
|
||||
},
|
||||
{
|
||||
"id": "beta",
|
||||
"name": "Beta"
|
||||
},
|
||||
{
|
||||
"id": "mvp",
|
||||
"name": "MVP"
|
||||
}
|
||||
],
|
||||
"asset_custody_type": [
|
||||
{
|
||||
"id": "non-custody",
|
||||
"name": "Non-custody"
|
||||
}
|
||||
],
|
||||
"sign_in_type_requirments": [
|
||||
{
|
||||
"id": "wallet",
|
||||
"name": "Wallet"
|
||||
},
|
||||
{
|
||||
"id": "email",
|
||||
"name": "Email"
|
||||
},
|
||||
{
|
||||
"id": "seed",
|
||||
"name": "Seed"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
export interface Category {
|
||||
id: string
|
||||
name: string
|
||||
usecases: string[]
|
||||
projectsCount: number
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export interface InputOption {
|
||||
label: string
|
||||
value: string
|
||||
value: string | number
|
||||
count?: number
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ export interface Member {
|
|||
role?: string
|
||||
link?: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
}[]
|
||||
company?: {
|
||||
name?: string
|
||||
link?: string
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { Member } from './member'
|
||||
import type { Team } from './team'
|
||||
import type { Fund } from './fund'
|
||||
import type { ClientDiversability } from './clientDiversability'
|
||||
import type { Audit } from './audit'
|
||||
|
@ -7,22 +7,25 @@ export interface Project {
|
|||
id: string
|
||||
name: string
|
||||
categories: string[]
|
||||
ecosystem?: string
|
||||
usecases: string[]
|
||||
ecosystem?: string[]
|
||||
product_readiness?: string
|
||||
security?: string
|
||||
have_token?: boolean
|
||||
token_link?: string
|
||||
tokens?: {
|
||||
name?: string
|
||||
symbol: string
|
||||
symbol?: string
|
||||
network?: string
|
||||
contract_address?: string
|
||||
link?: string
|
||||
token_link?: string
|
||||
[k: string]: unknown
|
||||
}[]
|
||||
description?: string
|
||||
project_type?: string
|
||||
product_launch_day?: string
|
||||
project_phase?: string
|
||||
sunset: boolean
|
||||
technology?: {
|
||||
type: string
|
||||
name?: string
|
||||
|
@ -41,6 +44,7 @@ export interface Project {
|
|||
github?: string
|
||||
docs?: string
|
||||
changelog?: string
|
||||
governance?: string
|
||||
forum?: string
|
||||
snapshot?: string
|
||||
lens?: string
|
||||
|
@ -62,17 +66,17 @@ export interface Project {
|
|||
}
|
||||
[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
|
||||
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?: {
|
||||
|
@ -81,7 +85,7 @@ export interface Project {
|
|||
data_usage?: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
team?: Member[]
|
||||
team?: Team
|
||||
storage?: {
|
||||
decentralized?: boolean
|
||||
[k: string]: unknown
|
||||
|
@ -89,7 +93,7 @@ export interface Project {
|
|||
tracebility?: {
|
||||
tracked_data?: string
|
||||
kyc?: boolean
|
||||
sign_in_type_requirments?: string
|
||||
sign_in_type_requirments?: string[]
|
||||
[k: string]: unknown
|
||||
}
|
||||
third_party_dependency?: string
|
||||
|
@ -97,6 +101,7 @@ export interface Project {
|
|||
audits?: Audit[]
|
||||
social_trust?: string
|
||||
technical_spof?: string
|
||||
assets_used?: string[]
|
||||
history?: {
|
||||
title?: string
|
||||
event_type?: string
|
||||
|
@ -104,7 +109,7 @@ export interface Project {
|
|||
time?: string
|
||||
link?: string
|
||||
[k: string]: unknown
|
||||
}
|
||||
}[]
|
||||
client_diversability?: ClientDiversability[]
|
||||
default_privacy?: boolean
|
||||
funding?: Fund[]
|
||||
|
@ -129,19 +134,19 @@ export interface ProjectShallow {
|
|||
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
|
||||
forum?: string
|
||||
github?: string
|
||||
website?: string
|
||||
twitter?: string
|
||||
coingecko?: string
|
||||
explorer?: string
|
||||
newsletter?: string
|
||||
readyness?: string
|
||||
team?: Team
|
||||
docs?: string
|
||||
audits?: Audit[]
|
||||
support?: number
|
||||
anonymity?: boolean
|
||||
}
|
||||
|
||||
export interface ProjectIndexable extends Project {
|
||||
|
|
19
types/team.ts
Normal file
19
types/team.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
export interface Team {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ export default defineConfig({
|
|||
black: '#000',
|
||||
green: '#B5E26B',
|
||||
red: '#FF5252',
|
||||
danger: '#FF0000',
|
||||
bg: {
|
||||
grey: '#ffffff33',
|
||||
dark_grey: '#161616',
|
||||
|
|
Loading…
Reference in a new issue