Spaces:
Running
Running
| <template> | |
| <div class="page-container"> | |
| <div class="title-container"> | |
| <span class="main-title">Agent Arena</span> | |
| </div> | |
| <div class="cards-container"> | |
| <!-- Add Agent Card --> | |
| <Card class="feature-card"> | |
| <template #title> | |
| <div class="card-title"> | |
| <i class="pi pi-robot"></i> | |
| <span>Add Trading Agent</span> | |
| </div> | |
| </template> | |
| <template #content> | |
| <div class="form-container"> | |
| <div class="field"> | |
| <label for="agentName">Agent Name</label> | |
| <InputText | |
| id="agentName" | |
| v-model="agentForm.name" | |
| placeholder="e.g., MyTradingAgent" | |
| class="w-full" | |
| /> | |
| </div> | |
| <div class="field"> | |
| <label for="agentEndpoint">API Endpoint</label> | |
| <InputText | |
| id="agentEndpoint" | |
| v-model="agentForm.endpoint" | |
| placeholder="https://api.example.com/trading-agent" | |
| class="w-full" | |
| /> | |
| </div> | |
| <div class="field"> | |
| <label for="agentDescription">Description (Optional)</label> | |
| <Textarea | |
| id="agentDescription" | |
| v-model="agentForm.description" | |
| placeholder="Brief description of your agent..." | |
| rows="3" | |
| class="w-full" | |
| /> | |
| </div> | |
| <Button | |
| label="Submit Agent" | |
| icon="pi pi-send" | |
| @click="submitAgent" | |
| :disabled="!agentForm.name || !agentForm.endpoint" | |
| :loading="agentForm.loading" | |
| class="w-full" | |
| /> | |
| <Message v-if="agentForm.message" :severity="agentForm.messageType" :closable="false"> | |
| {{ agentForm.message }} | |
| </Message> | |
| </div> | |
| </template> | |
| </Card> | |
| <!-- Add Asset Card --> | |
| <Card class="feature-card"> | |
| <template #title> | |
| <div class="card-title"> | |
| <i class="pi pi-chart-line"></i> | |
| <span>Request Asset</span> | |
| </div> | |
| </template> | |
| <template #content> | |
| <div class="form-container"> | |
| <div class="field"> | |
| <label for="assetSymbol">Asset Symbol</label> | |
| <InputText | |
| id="assetSymbol" | |
| v-model="assetForm.symbol" | |
| placeholder="e.g., NVDA, GOOGL, SOL" | |
| class="w-full" | |
| /> | |
| </div> | |
| <div class="field"> | |
| <label for="assetType">Asset Type</label> | |
| <Dropdown | |
| id="assetType" | |
| v-model="assetForm.type" | |
| :options="assetTypes" | |
| optionLabel="label" | |
| optionValue="value" | |
| placeholder="Select asset type" | |
| class="w-full" | |
| /> | |
| </div> | |
| <div class="field"> | |
| <label for="assetReason">Reason for Request (Optional)</label> | |
| <Textarea | |
| id="assetReason" | |
| v-model="assetForm.reason" | |
| placeholder="Why would you like to see this asset?" | |
| rows="3" | |
| class="w-full" | |
| /> | |
| </div> | |
| <Button | |
| label="Request Asset" | |
| icon="pi pi-plus" | |
| @click="submitAsset" | |
| :disabled="!assetForm.symbol || !assetForm.type" | |
| :loading="assetForm.loading" | |
| class="w-full" | |
| /> | |
| <Message v-if="assetForm.message" :severity="assetForm.messageType" :closable="false"> | |
| {{ assetForm.message }} | |
| </Message> | |
| </div> | |
| </template> | |
| </Card> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| import Card from 'primevue/card' | |
| import InputText from 'primevue/inputtext' | |
| import Textarea from 'primevue/textarea' | |
| import Dropdown from 'primevue/dropdown' | |
| import Button from 'primevue/button' | |
| import Message from 'primevue/message' | |
| import emailjs from 'emailjs-com' | |
| export default { | |
| name: 'AddAssetsView', | |
| components: { | |
| Card, | |
| InputText, | |
| Textarea, | |
| Dropdown, | |
| Button, | |
| Message | |
| }, | |
| data() { | |
| return { | |
| agentForm: { | |
| name: '', | |
| endpoint: '', | |
| description: '', | |
| loading: false, | |
| message: '', | |
| messageType: 'info' | |
| }, | |
| assetForm: { | |
| symbol: '', | |
| type: '', | |
| reason: '', | |
| loading: false, | |
| message: '', | |
| messageType: 'info' | |
| }, | |
| assetTypes: [ | |
| { label: 'Stock', value: 'stock' }, | |
| { label: 'Cryptocurrency', value: 'crypto' }, | |
| { label: 'ETF', value: 'etf' }, | |
| { label: 'Other', value: 'other' } | |
| ] | |
| } | |
| }, | |
| methods: { | |
| async submitAgent() { | |
| this.agentForm.loading = true | |
| this.agentForm.message = '' | |
| try { | |
| // 保存到 localStorage 以便管理员查看 | |
| const agentSubmissions = JSON.parse(localStorage.getItem('agentSubmissions') || '[]') | |
| agentSubmissions.push({ | |
| name: this.agentForm.name, | |
| endpoint: this.agentForm.endpoint, | |
| description: this.agentForm.description, | |
| timestamp: new Date().toISOString() | |
| }) | |
| localStorage.setItem('agentSubmissions', JSON.stringify(agentSubmissions)) | |
| this.agentForm.message = 'Agent submitted successfully! We will review it soon.' | |
| this.agentForm.messageType = 'success' | |
| // 滚动到消息位置 | |
| this.$nextTick(() => { | |
| const messageEl = this.$el.querySelector('.p-message') | |
| if (messageEl) { | |
| messageEl.scrollIntoView({ behavior: 'smooth', block: 'center' }) | |
| } | |
| }) | |
| // Clear form | |
| setTimeout(() => { | |
| this.agentForm.name = '' | |
| this.agentForm.endpoint = '' | |
| this.agentForm.description = '' | |
| this.agentForm.message = '' | |
| }, 5000) | |
| } catch (error) { | |
| console.error('Error submitting agent:', error) | |
| this.agentForm.message = 'Failed to submit agent. Please try again.' | |
| this.agentForm.messageType = 'error' | |
| // 滚动到消息位置 | |
| this.$nextTick(() => { | |
| const messageEl = this.$el.querySelector('.p-message') | |
| if (messageEl) { | |
| messageEl.scrollIntoView({ behavior: 'smooth', block: 'center' }) | |
| } | |
| }) | |
| } finally { | |
| this.agentForm.loading = false | |
| } | |
| }, | |
| async submitAsset() { | |
| this.assetForm.loading = true | |
| this.assetForm.message = '' | |
| try { | |
| // 保存到 localStorage | |
| const assetRequest = { | |
| symbol: this.assetForm.symbol.toUpperCase(), | |
| type: this.assetForm.type, | |
| reason: this.assetForm.reason, | |
| timestamp: new Date().toISOString(), | |
| votes: 1 | |
| } | |
| // 获取现有的请求列表 | |
| let requests = [] | |
| try { | |
| const stored = localStorage.getItem('assetRequests') | |
| if (stored) { | |
| requests = JSON.parse(stored) | |
| } | |
| } catch (e) { | |
| console.error('Error reading localStorage:', e) | |
| } | |
| // 检查是否已存在相同的 symbol | |
| const existingIndex = requests.findIndex(r => r.symbol.toUpperCase() === assetRequest.symbol.toUpperCase()) | |
| if (existingIndex >= 0) { | |
| // 如果已存在,增加投票数 | |
| requests[existingIndex].votes += 1 | |
| requests[existingIndex].timestamp = assetRequest.timestamp | |
| } else { | |
| // 否则添加新请求 | |
| requests.push(assetRequest) | |
| } | |
| // 保存到 localStorage | |
| localStorage.setItem('assetRequests', JSON.stringify(requests)) | |
| // 延迟跳转,让用户看到动画 | |
| await new Promise(resolve => setTimeout(resolve, 500)) | |
| // 跳转到结果页面 | |
| this.$router.push('/asset-requests') | |
| } catch (error) { | |
| console.error('Error submitting asset:', error) | |
| this.assetForm.message = 'Failed to submit request. Please try again.' | |
| this.assetForm.messageType = 'error' | |
| // 滚动到错误消息 | |
| this.$nextTick(() => { | |
| const messageEl = this.$el.querySelectorAll('.p-message')[1] | |
| if (messageEl) { | |
| messageEl.scrollIntoView({ behavior: 'smooth', block: 'center' }) | |
| } | |
| }) | |
| } finally { | |
| this.assetForm.loading = false | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style scoped> | |
| .page-container { | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| padding: 1rem; | |
| } | |
| .title-container { | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| } | |
| .main-title { | |
| font-size: 2rem; | |
| letter-spacing: -0.02em; | |
| font-weight: 800; | |
| color: #1f1f33; | |
| } | |
| .cards-container { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(450px, 1fr)); | |
| gap: 2rem; | |
| padding: 0 1rem; | |
| } | |
| .feature-card { | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| } | |
| .feature-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); | |
| } | |
| .card-title { | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| font-size: 1.25rem; | |
| font-weight: 700; | |
| color: #1f1f33; | |
| } | |
| .card-title i { | |
| font-size: 1.5rem; | |
| color: var(--primary-color); | |
| } | |
| .form-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1.5rem; | |
| } | |
| .field { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 0.5rem; | |
| } | |
| .field label { | |
| font-weight: 600; | |
| color: #4b5563; | |
| font-size: 0.95rem; | |
| } | |
| :deep(.p-inputtext), | |
| :deep(.p-dropdown), | |
| :deep(.p-inputtextarea) { | |
| border-radius: 8px; | |
| border: 1px solid #d1d5db; | |
| padding: 0.75rem; | |
| } | |
| :deep(.p-inputtext:focus), | |
| :deep(.p-dropdown:focus), | |
| :deep(.p-inputtextarea:focus) { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1); | |
| } | |
| :deep(.p-button) { | |
| border-radius: 8px; | |
| padding: 0.75rem; | |
| font-weight: 600; | |
| margin-top: 0.5rem; | |
| } | |
| :deep(.p-message) { | |
| border-radius: 8px; | |
| margin-top: 0.5rem; | |
| } | |
| @media (max-width: 768px) { | |
| .cards-container { | |
| grid-template-columns: 1fr; | |
| padding: 0 0.5rem; | |
| } | |
| .main-title { | |
| font-size: 1.5rem; | |
| } | |
| } | |
| </style> |