Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: 构建编译
name: Build Application

on:
workflow_dispatch:
Expand Down
36 changes: 35 additions & 1 deletion src/apis/frontiter.api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AxiosInstance } from 'axios'
import request, { PaginationResponse, TPagination } from './request'
import { data } from 'react-router-dom'

interface Response<T> {
data: T
Expand All @@ -15,6 +16,23 @@ export interface TaskRewardInfo {
reward_value: number
}

export type ActiveStatus = 'ACTIVE' | 'INACTIVE' | 'COMPLETED'
export interface FrontierActivityInfoItem {
activity_id: string
start_time: string
end_time: string
reward_mode: 'EQUAL_SPLIT_ON_END' | 'FIRST_COME_FIRST_SERVE'
min_ranking_grade: string
days_left: number
total_asset_amount: number
max_reward_count: number
reward_asset_type: string
participants: number
submissions: number
status: ActiveStatus
rules?: readonly string[]
}

export interface TaskDetail {
frontier_id: string
task_id: string
Expand All @@ -31,6 +49,7 @@ export interface TaskDetail {
question_status?: number // 1: available, 2: no more questions, 3. need to change question group
data_requirements: unknown
reward_info: readonly TaskRewardInfo[]

status: 'PENDING' | 'SUBMITTED' | 'REFUSED' | 'ADOPT'
txHashUrl: string
}
Expand All @@ -45,6 +64,13 @@ export interface FrontierListItem {
reputation_permission: number
status: string
title: string
activities: FrontierActivityInfoItem[]
// start
// total_asset_amount: number
// reward_asset_type: string
// min_ranking_grade: string
// days_left: number
// end
template_ext?: {
template_id: string
gif_resource?: string
Expand Down Expand Up @@ -157,7 +183,15 @@ class frontier {
}

async getFrontiers(): Promise<Response<FrontierListItem[]>> {
const res = await this.request.post('/v2/frontier/list ')
const res = await this.request.post('/v2/frontier/list ', data)
return res.data
}

async getFrontierActivityInfo(data: {
frontier_id: string
status?: ActiveStatus
}): Promise<Response<FrontierActivityInfoItem[]>> {
const res = await this.request.post('/v2/frontier/activity/info', data)
return res.data
}

Expand Down
3 changes: 3 additions & 0 deletions src/assets/home/badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/home/dollar-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/home/hourglass.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
139 changes: 139 additions & 0 deletions src/components/home/activity-info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { cn } from '@udecode/cn'
import { useParams } from 'react-router-dom'
import { useEffect, useState } from 'react'
import dayjs from 'dayjs'

import { ChevronUp } from 'lucide-react'

import { formatNumber } from '@/utils/str'
import frontierApi, { FrontierActivityInfoItem } from '@/apis/frontiter.api'

export default function ActivityInfo({ className }: { className?: string }) {
const { frontier_id } = useParams()
const [activityInfoList, setActivityInfoList] = useState<FrontierActivityInfoItem[] | []>([])

async function getFrontierActivityInfo(frontier_id: string) {
const res = await frontierApi.getFrontierActivityInfo({ frontier_id })
if (res.errorCode === 0) {
setActivityInfoList(res.data)
console.log(res.data)
}
}
useEffect(() => {
if (!frontier_id) return
getFrontierActivityInfo(frontier_id)
}, [frontier_id])

return (
<div className={cn('mt-4', activityInfoList.length > 0 ? 'block' : 'hidden', className)}>
<ul className="space-y-4">
{activityInfoList.map((item) => (
<li key={item.activity_id} className="rounded-2xl border border-[#FFFFFF1F] p-5">
<header
className={cn(
'grid grid-cols-4 gap-4 rounded-xl p-3 text-xs',
item.status === 'COMPLETED' ? 'bg-[#252532]' : 'bg-[#875DFF]'
)}
>
<div>
<div className="mb-[2px] text-[#FFFFFF/75]">Duration</div>
<div className="text-base font-bold">
{dayjs(item.start_time).format('YYYY-MM-DD')} to {dayjs(item.end_time).format('YYYY-MM-DD')}
</div>
</div>
<div>
<div className="mb-[2px] text-[#FFFFFF/75]">Min Quality</div>
<div className="flex size-7 items-center justify-center rounded-full bg-gradient-to-r from-[#FEC53C] to-[#E7B231] text-xs font-semibold text-[#1C1C26]">
{item.min_ranking_grade}
</div>
</div>
<div>
<div className="mb-[2px] text-[#FFFFFF/75]">Total Rewards</div>
<div className="text-base font-bold">
{formatNumber(item.total_asset_amount || 0)}
{item.reward_asset_type}
</div>
</div>
{item.reward_mode === 'EQUAL_SPLIT_ON_END' ? (
<div>
<div className="mb-[2px] text-[#FFFFFF/75]">Total Submissions</div>
<div className="text-base font-bold">{formatNumber(item.submissions || 0)}</div>
</div>
) : (
<div>
<div className="mb-[2px] text-[#FFFFFF/75]">Target</div>
<div className="flex items-center gap-[6px] text-base font-bold">
<svg width="166" height="8" viewBox="0 0 166 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="166" height="8" rx="4" fill="white" />
<rect
width={(item.submissions / item.max_reward_count) * 166}
height="8"
rx="4"
fill="url(#paint0_linear_37683_29699)"
/>
<defs>
<linearGradient
id="paint0_linear_37683_29699"
x1="0"
y1="4"
x2="128.451"
y2="4"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#58E6F3" />
<stop offset="0.348675" stop-color="#79A5FC" />
<stop offset="0.671472" stop-color="#D35BFC" />
<stop offset="1" stop-color="#FEBCCC" />
</linearGradient>
</defs>
</svg>
{formatNumber(item.submissions || 0)}/{formatNumber(item.max_reward_count || 0)}
</div>
</div>
)}
</header>
<Rules activity={item} />
</li>
))}
</ul>
</div>
)
}

function getRulesText(activity: FrontierActivityInfoItem) {
return [
'Multiple valid submissions count for reward sharing',
`Minimum data quality requirement: Grade ${activity.min_ranking_grade}`,
'Below standard submissions receive points reward',
'Quality assessment based on accuracy and completeness',
activity.reward_mode === 'FIRST_COME_FIRST_SERVE'
? 'First come, first served: Fixed rewards for reaching baseline score during activity period. Once required quantity is reached, no more rewards will be distributed even if activity continues.Below standard gets points reward'
: 'Divide and share mode: Multiple submissions valid, each counts for sharing. Below standard gets points reward.'
]
}

function Rules({ activity }: { activity: FrontierActivityInfoItem }) {
const rules = getRulesText(activity)
const [showRules, setShowRules] = useState(false)

return (
<div className="mt-4 rounded-xl bg-[#252532] p-4">
<h3 className="mb-2 flex items-center justify-between text-base font-semibold">
<span>📋 Activity Rules</span>{' '}
<ChevronUp
size={24}
className={cn('cursor-pointer transition-all', showRules ? 'rotate-180' : '')}
onClick={() => setShowRules(!showRules)}
/>
</h3>
<ul
className={cn(
'list-outside list-disc overflow-hidden pl-5 text-sm leading-[22px] text-[#BBBBBE] transition-all',
showRules ? 'h-auto' : 'h-0'
)}
>
{rules?.map((rule) => <li key={rule}>{rule}</li>)}
</ul>
</div>
)
}
71 changes: 56 additions & 15 deletions src/components/home/frontiers.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import arrowRight from '@/assets/icons/arrow-right.svg'
import { message, Spin } from 'antd'
import { useNavigate } from 'react-router-dom'
import { useEffect, useMemo, useState } from 'react'

import arrowRight from '@/assets/icons/arrow-right.svg?url'
import badgeIcon from '@/assets/home/badge.svg#file'
import DollarCircle from '@/assets/home/dollar-circle.svg?react'
import Hourglass from '@/assets/home/hourglass.svg?react'

import { FrontierListItem } from '@/apis/frontiter.api'
import { frontierStoreActions, useFrontierStore } from '@/stores/frontier.store'
import { message, Spin } from 'antd'

const Frontiers = () => {
const navigate = useNavigate()
Expand All @@ -12,11 +17,20 @@ const Frontiers = () => {
const { frontierList } = useFrontierStore()

const displayFrontiers = useMemo(() => {
return frontierList.filter((item) => {
return !['FOOD_TPL_000002', 'FOOD_TPL_000003', 'FOOD_TPL_000004', 'FOOD_TPL_000005'].includes(
item.template_ext?.template_id || ''
)
})
return frontierList
.filter((item) => {
return !['FOOD_TPL_000002', 'FOOD_TPL_000003', 'FOOD_TPL_000004', 'FOOD_TPL_000005'].includes(
item.template_ext?.template_id || ''
)
})
.map((item) => {
return {
...item,
activities: item.activities?.filter((activity) => {
return activity.status === 'ACTIVE'
})
}
})
}, [frontierList])

async function getFrontiers() {
Expand Down Expand Up @@ -54,7 +68,7 @@ const Frontiers = () => {
{displayFrontiers.map((item) => (
<div
key={item.title}
className="group relative aspect-[269/243] w-full cursor-pointer overflow-hidden rounded-2xl"
className="group relative aspect-[269/243] cursor-pointer overflow-hidden rounded-2xl"
onClick={() => handleFrontierClick(item)}
>
<img
Expand All @@ -63,18 +77,45 @@ const Frontiers = () => {
className="size-full object-cover transition-all group-hover:scale-[1.2]"
/>
<div
className="absolute top-0 flex size-full flex-col justify-end gap-3 p-4"
className="absolute top-0 flex size-full flex-col justify-end gap-3 py-2"
style={{
background: 'linear-gradient(180deg, rgba(0, 0, 0, 0) 21.88%, #000000 100%)'
}}
>
{item.activities?.[0] && (
<div
className="absolute right-4 top-2 flex size-8 items-center justify-center bg-cover bg-center text-base font-bold text-white"
style={{ backgroundImage: `url(${badgeIcon})` }}
>
{item.activities?.[0].min_ranking_grade || 'S'}
</div>
)}
<div>
<h2 className="mb-2 text-base font-bold">{item.title}</h2>
<div className="line-clamp-2 text-[#A4A4A8]">{item.description.frontier_desc}</div>
</div>
<div className="flex h-8 w-[104px] flex-none cursor-pointer flex-row items-center justify-center rounded-full bg-primary">
<span>Start</span>
<img src={arrowRight} alt="" />
<h2 className="mb-2 px-4 text-base font-bold">{item.title}</h2>
<div className="relative bg-[#0000000A] px-4 py-2">
<div className="absolute inset-0 size-full blur-sm" />
<div className="relative flex items-center justify-between gap-5">
{item.activities?.[0] ? (
<div className="text-sm">
<div className="flex items-center">
<DollarCircle className="mr-1" />
<span>{item.activities?.[0].total_asset_amount || 0}</span>
<span className="mr-3">{item.activities?.[0].reward_asset_type}</span>
</div>
<div className="mt-1 flex items-center">
<Hourglass className="mr-1" />
<span>{item.activities?.[0].days_left || 0}</span>D
</div>
</div>
) : (
<div className="line-clamp-2 text-[#A4A4A8]">{item.description.frontier_desc}</div>
)}
<div className="flex h-[30px] w-[98px] flex-none cursor-pointer items-center justify-center gap-1 rounded-full bg-primary text-xs">
<span>Start</span>
<img src={arrowRight} alt="" />
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down
15 changes: 0 additions & 15 deletions src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { lazy, useEffect } from 'react'

// layouts
import FrontierLayout from '@/layouts/frontier-layout'
// import SettingsLayout from '@/layouts/settings-layout'
import UserInfoLayout from '@/layouts/userinfo-layout'
import AppLayout from '@/layouts/app-layout'
import ArenaLayout from '@/layouts/arena-layout'
Expand All @@ -22,13 +21,6 @@ const NewJourney = lazy(() => import('@/views/new-journey'))
const ActivityGroup = lazy(() => import('@/views/quest/activity-group'))
const Activity = lazy(() => import('@/views/quest/activity'))

// settings
// const SettingAccount = lazy(() => import('@/views/settings/account'))
// const SettingReward = lazy(() => import('@/views/settings/reward'))
// const SettingReputation = lazy(() => import('@/views/settings/reputation'))
// const SettingUserNFT = lazy(() => import('@/views/settings/user-nft'))
// const SettingUserSBT = lazy(() => import('@/views/settings/user-sbt'))

// user info
const UserInfo = lazy(() => import('@/views/userinfo/index'))
const UserInfoReward = lazy(() => import('@/views/userinfo/reward'))
Expand Down Expand Up @@ -122,13 +114,6 @@ export default function Router() {
<Route path="referral" element={<AppReferral />}></Route>
<Route path="journey" element={<NewJourney />} />

{/* <Route path="settings" element={<SettingsLayout />}>
<Route path="account" element={<SettingAccount />} />
<Route path="reward" element={<SettingReward />} />
<Route path="reputation" element={<SettingReputation />} />
<Route path="nft" element={<SettingUserNFT />} /> */}
{/* <Route path="sbt" element={<SettingUserSBT />} /> */}
{/* </Route> */}
<Route path="settings" element={<UserInfoLayout />}>
<Route index element={<UserInfo />} />
<Route path="account" element={<UserInfo />} />
Expand Down
Loading