@oasisomniverse/angular 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +37 -0
- package/src/components/avatar/avatar-wallet.component.ts +49 -0
- package/src/components/avatar/edit-avatar.component.ts +28 -0
- package/src/components/avatar/search-avatar.component.ts +45 -0
- package/src/components/avatar/view-avatar.component.ts +49 -0
- package/src/components/avatar-connect/avatar-connect.component.ts +78 -0
- package/src/components/avatar-profile/avatar-profile.component.ts +146 -0
- package/src/components/coming-soon/coming-soon.component.ts +27 -0
- package/src/components/confirmation/confirmation.component.ts +38 -0
- package/src/components/contact/contact.component.ts +64 -0
- package/src/components/data-screen/add-data.component.ts +50 -0
- package/src/components/data-screen/cross-chain-management.component.ts +28 -0
- package/src/components/data-screen/load-data.component.ts +28 -0
- package/src/components/data-screen/manage-data.component.ts +50 -0
- package/src/components/data-screen/off-chain-management.component.ts +28 -0
- package/src/components/data-screen/search-data.component.ts +40 -0
- package/src/components/eggs/eggs.component.ts +76 -0
- package/src/components/eggs/manage-eggs.component.ts +50 -0
- package/src/components/eggs/search-eggs.component.ts +40 -0
- package/src/components/eggs/view-eggs.component.ts +49 -0
- package/src/components/forgot-password/forgot-password.component.ts +80 -0
- package/src/components/game/game.component.ts +58 -0
- package/src/components/game/search-profiles.component.ts +36 -0
- package/src/components/game/view-achievements.component.ts +48 -0
- package/src/components/game/view-leagues.component.ts +48 -0
- package/src/components/game/view-tournaments.component.ts +49 -0
- package/src/components/hyperdrive/hyperdrive.component.ts +81 -0
- package/src/components/karma/search-karma.component.ts +36 -0
- package/src/components/karma/view-avatar-karma.component.ts +51 -0
- package/src/components/karma/view-karma.component.ts +46 -0
- package/src/components/karma/vote-karma.component.ts +48 -0
- package/src/components/karma-panel/karma-panel.component.ts +87 -0
- package/src/components/karma-toast/karma-toast.component.ts +33 -0
- package/src/components/login/login.component.ts +95 -0
- package/src/components/map/add-2d-object-map.component.ts +42 -0
- package/src/components/map/add-3d-object-map.component.ts +43 -0
- package/src/components/map/add-quest-to-map.component.ts +42 -0
- package/src/components/map/download-our-world.component.ts +28 -0
- package/src/components/map/manage-map.component.ts +28 -0
- package/src/components/map/map.component.ts +70 -0
- package/src/components/map/plot-route-on-map.component.ts +42 -0
- package/src/components/map/search-map.component.ts +44 -0
- package/src/components/map/view-global-3d-map.component.ts +28 -0
- package/src/components/map/view-halons-on-map.component.ts +28 -0
- package/src/components/map/view-oapp-on-map.component.ts +28 -0
- package/src/components/map/view-quest-on-map.component.ts +28 -0
- package/src/components/menu-message/menu-message.component.ts +69 -0
- package/src/components/messages/message.component.ts +59 -0
- package/src/components/messaging/messaging.component.ts +89 -0
- package/src/components/mission/manage-mission.component.ts +43 -0
- package/src/components/mission/mission.component.ts +63 -0
- package/src/components/mission/search-mission.component.ts +36 -0
- package/src/components/mission/view-mission.component.ts +49 -0
- package/src/components/modal/modal.component.ts +28 -0
- package/src/components/navbar/navbar.component.ts +64 -0
- package/src/components/nft/contact-popup.component.ts +41 -0
- package/src/components/nft/manage-oasis-nft.component.ts +43 -0
- package/src/components/nft/nft.component.ts +70 -0
- package/src/components/nft/purchase-oasis-nft.component.ts +53 -0
- package/src/components/nft/purchase-oasis-virtual-land-nft.component.ts +28 -0
- package/src/components/nft/search-oasis-nft.component.ts +36 -0
- package/src/components/nft/solana.component.ts +28 -0
- package/src/components/nft/view-oasis-nft.component.ts +52 -0
- package/src/components/oapp/create-oapp.component.ts +43 -0
- package/src/components/oapp/deploy-oapp.component.ts +42 -0
- package/src/components/oapp/download-our-world.component.ts +28 -0
- package/src/components/oapp/edit-oapp.component.ts +41 -0
- package/src/components/oapp/install-oapp.component.ts +36 -0
- package/src/components/oapp/launch-oapp.component.ts +58 -0
- package/src/components/oapp/manage-oapp.component.ts +42 -0
- package/src/components/oapp/oapp.component.ts +70 -0
- package/src/components/oapp/search-oapp.component.ts +36 -0
- package/src/components/oasis-card/oasis-card.component.ts +30 -0
- package/src/components/onet/onet.component.ts +77 -0
- package/src/components/onode/onode.component.ts +73 -0
- package/src/components/provider/activity-pub.component.ts +44 -0
- package/src/components/provider/compare-provider-speeds.component.ts +48 -0
- package/src/components/provider/eosio.component.ts +44 -0
- package/src/components/provider/ethereum.component.ts +44 -0
- package/src/components/provider/holochain.component.ts +44 -0
- package/src/components/provider/ipfs.component.ts +44 -0
- package/src/components/provider/manage-auto-fail-over.component.ts +42 -0
- package/src/components/provider/manage-auto-replicaton.component.ts +42 -0
- package/src/components/provider/manage-load-balancing.component.ts +42 -0
- package/src/components/provider/manage-providers.component.ts +28 -0
- package/src/components/provider/mongo-db.component.ts +44 -0
- package/src/components/provider/neo4j.component.ts +44 -0
- package/src/components/provider/search-providers.component.ts +36 -0
- package/src/components/provider/seeds-provider.component.ts +42 -0
- package/src/components/provider/solid.component.ts +44 -0
- package/src/components/provider/sql-lite.component.ts +44 -0
- package/src/components/provider/three-fold.component.ts +44 -0
- package/src/components/provider/view-provider-stats.component.ts +61 -0
- package/src/components/provider/view-providers.component.ts +54 -0
- package/src/components/provider-dropdown/provider-dropdown.component.ts +64 -0
- package/src/components/providers/providers.component.ts +60 -0
- package/src/components/quest/manage-quest.component.ts +43 -0
- package/src/components/quest/quest.component.ts +65 -0
- package/src/components/quest/search-quest.component.ts +36 -0
- package/src/components/quest/view-quest.component.ts +49 -0
- package/src/components/reset-password/reset-password.component.ts +68 -0
- package/src/components/search-avatars/search-avatars.component.ts +80 -0
- package/src/components/seeds/accept-invite.component.ts +44 -0
- package/src/components/seeds/donate-seeds.component.ts +42 -0
- package/src/components/seeds/manage-seeds.component.ts +42 -0
- package/src/components/seeds/pay-with-seeds.component.ts +41 -0
- package/src/components/seeds/reward-seeds.component.ts +41 -0
- package/src/components/seeds/search-seeds.component.ts +36 -0
- package/src/components/seeds/seeds.component.ts +75 -0
- package/src/components/seeds/send-invite.component.ts +41 -0
- package/src/components/seeds/view-organizations.component.ts +55 -0
- package/src/components/seeds/view-seeds.component.ts +78 -0
- package/src/components/settings/settings.component.ts +96 -0
- package/src/components/signup/signup.component.ts +118 -0
- package/src/components/star-field/star-field.component.ts +44 -0
- package/src/components/verify-email/verify-email.component.ts +48 -0
- package/src/components/wallet/wallet.component.ts +83 -0
- package/src/index.ts +145 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'oasis-cross-chain-management',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
template: `
|
|
9
|
+
<div class="popup-shell">
|
|
10
|
+
<div class="popup-icon">⛓️</div>
|
|
11
|
+
<h2 class="popup-title">Cross Chain Management</h2>
|
|
12
|
+
<p class="popup-body">UI Coming Soon — access this feature via the OASIS API from the Developer menu.</p>
|
|
13
|
+
<button class="popup-btn" (click)="close.emit()">OK</button>
|
|
14
|
+
</div>
|
|
15
|
+
`,
|
|
16
|
+
styles: [`
|
|
17
|
+
.popup-shell{display:flex;flex-direction:column;align-items:center;gap:16px;padding:24px;text-align:center}
|
|
18
|
+
.popup-icon{font-size:48px}
|
|
19
|
+
.popup-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
20
|
+
.popup-body{font-size:13px;color:#a8bfd8;line-height:1.6;margin:0;max-width:380px}
|
|
21
|
+
.popup-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:10px 28px;cursor:pointer;transition:opacity .2s}
|
|
22
|
+
.popup-btn:hover{opacity:.85}
|
|
23
|
+
`]
|
|
24
|
+
})
|
|
25
|
+
export class CrossChainManagementComponent {
|
|
26
|
+
@Input() show = false;
|
|
27
|
+
@Output() close = new EventEmitter<void>();
|
|
28
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'oasis-load-data',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
template: `
|
|
9
|
+
<div class="popup-shell">
|
|
10
|
+
<div class="popup-icon">📂</div>
|
|
11
|
+
<h2 class="popup-title">Load Data</h2>
|
|
12
|
+
<p class="popup-body">UI Coming Soon — access this feature via the OASIS API from the Developer menu.</p>
|
|
13
|
+
<button class="popup-btn" (click)="close.emit()">OK</button>
|
|
14
|
+
</div>
|
|
15
|
+
`,
|
|
16
|
+
styles: [`
|
|
17
|
+
.popup-shell{display:flex;flex-direction:column;align-items:center;gap:16px;padding:24px;text-align:center}
|
|
18
|
+
.popup-icon{font-size:48px}
|
|
19
|
+
.popup-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
20
|
+
.popup-body{font-size:13px;color:#a8bfd8;line-height:1.6;margin:0;max-width:380px}
|
|
21
|
+
.popup-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:10px 28px;cursor:pointer;transition:opacity .2s}
|
|
22
|
+
.popup-btn:hover{opacity:.85}
|
|
23
|
+
`]
|
|
24
|
+
})
|
|
25
|
+
export class LoadDataComponent {
|
|
26
|
+
@Input() show = false;
|
|
27
|
+
@Output() close = new EventEmitter<void>();
|
|
28
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'oasis-manage-data',
|
|
7
|
+
standalone: true,
|
|
8
|
+
imports: [CommonModule, FormsModule],
|
|
9
|
+
template: `
|
|
10
|
+
<div class="mf-shell">
|
|
11
|
+
<div class="mf-header"><span>🗄️</span><h2 class="mf-title">Manage Data</h2></div>
|
|
12
|
+
<div class="mf-body">
|
|
13
|
+
<div class="mf-field"><label class="mf-lbl">Data Name</label><input class="mf-inp" [(ngModel)]="form['dataname']" placeholder="Data Name..." /></div>
|
|
14
|
+
<div class="mf-field"><label class="mf-lbl">Data Type</label><input class="mf-inp" [(ngModel)]="form['datatype']" placeholder="Data Type..." /></div>
|
|
15
|
+
<div class="mf-field"><label class="mf-lbl">Value</label><input class="mf-inp" [(ngModel)]="form['value']" placeholder="Value..." /></div>
|
|
16
|
+
<div class="mf-field"><label class="mf-lbl">Provider</label><input class="mf-inp" [(ngModel)]="form['provider']" placeholder="Provider..." /></div>
|
|
17
|
+
<div class="mf-field"><label class="mf-lbl">Access Level</label><input class="mf-inp" [(ngModel)]="form['accesslevel']" placeholder="Access Level..." /></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="mf-footer">
|
|
20
|
+
<button class="mf-save" (click)="save()">Save</button>
|
|
21
|
+
<button class="mf-cancel" (click)="close.emit()">Cancel</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
styles: [`
|
|
26
|
+
.mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
|
|
27
|
+
.mf-header{display:flex;align-items:center;gap:10px}
|
|
28
|
+
.mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
29
|
+
.mf-body{display:flex;flex-direction:column;gap:12px}
|
|
30
|
+
.mf-field{display:flex;flex-direction:column;gap:5px}
|
|
31
|
+
.mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
|
|
32
|
+
.mf-inp{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:8px;color:#fff;font-size:13px;padding:10px 14px;outline:none}
|
|
33
|
+
.mf-inp:focus{border-color:rgba(0,200,255,.6)}
|
|
34
|
+
.mf-footer{display:flex;gap:10px;justify-content:flex-end}
|
|
35
|
+
.mf-save{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;padding:10px 22px;cursor:pointer}
|
|
36
|
+
.mf-cancel{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:10px 18px;cursor:pointer}
|
|
37
|
+
`]
|
|
38
|
+
})
|
|
39
|
+
export class ManageDataComponent {
|
|
40
|
+
@Input() show = false;
|
|
41
|
+
@Output() close = new EventEmitter<void>();
|
|
42
|
+
form: Record<string,string> = {
|
|
43
|
+
'dataname': '',
|
|
44
|
+
'datatype': '',
|
|
45
|
+
'value': '',
|
|
46
|
+
'provider': '',
|
|
47
|
+
'accesslevel': ''
|
|
48
|
+
};
|
|
49
|
+
save() { this.close.emit(); }
|
|
50
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'oasis-off-chain-management',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
template: `
|
|
9
|
+
<div class="popup-shell">
|
|
10
|
+
<div class="popup-icon">🔗</div>
|
|
11
|
+
<h2 class="popup-title">Off-Chain Management</h2>
|
|
12
|
+
<p class="popup-body">UI Coming Soon — access this feature via the OASIS API from the Developer menu.</p>
|
|
13
|
+
<button class="popup-btn" (click)="close.emit()">OK</button>
|
|
14
|
+
</div>
|
|
15
|
+
`,
|
|
16
|
+
styles: [`
|
|
17
|
+
.popup-shell{display:flex;flex-direction:column;align-items:center;gap:16px;padding:24px;text-align:center}
|
|
18
|
+
.popup-icon{font-size:48px}
|
|
19
|
+
.popup-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
20
|
+
.popup-body{font-size:13px;color:#a8bfd8;line-height:1.6;margin:0;max-width:380px}
|
|
21
|
+
.popup-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:10px 28px;cursor:pointer;transition:opacity .2s}
|
|
22
|
+
.popup-btn:hover{opacity:.85}
|
|
23
|
+
`]
|
|
24
|
+
})
|
|
25
|
+
export class OffChainManagementComponent {
|
|
26
|
+
@Input() show = false;
|
|
27
|
+
@Output() close = new EventEmitter<void>();
|
|
28
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'oasis-search-data',
|
|
7
|
+
standalone: true,
|
|
8
|
+
imports: [CommonModule, FormsModule],
|
|
9
|
+
template: `
|
|
10
|
+
<div class="sf-shell">
|
|
11
|
+
<div class="sf-header"><span class="sf-icon">🔍</span><h2 class="sf-title">Search Data</h2></div>
|
|
12
|
+
<div class="sf-row">
|
|
13
|
+
<input class="sf-input" [(ngModel)]="query" placeholder="Search by name, type or provider..." (keyup.enter)="search()" />
|
|
14
|
+
<button class="sf-btn" (click)="search()">Search</button>
|
|
15
|
+
</div>
|
|
16
|
+
@for (r of results(); track r) { <div class="sf-result">{{ r }}</div> }
|
|
17
|
+
<div class="sf-footer"><button class="sf-cancel" (click)="close.emit()">Close</button></div>
|
|
18
|
+
</div>
|
|
19
|
+
`,
|
|
20
|
+
styles: [`
|
|
21
|
+
.sf-shell{display:flex;flex-direction:column;gap:14px;padding:20px}
|
|
22
|
+
.sf-header{display:flex;align-items:center;gap:10px}
|
|
23
|
+
.sf-icon{font-size:24px}
|
|
24
|
+
.sf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
25
|
+
.sf-row{display:flex;gap:10px}
|
|
26
|
+
.sf-input{flex:1;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:8px;color:#fff;font-size:13px;padding:10px 14px;outline:none}
|
|
27
|
+
.sf-input:focus{border-color:rgba(0,200,255,.6)}
|
|
28
|
+
.sf-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;padding:10px 20px;cursor:pointer;white-space:nowrap}
|
|
29
|
+
.sf-result{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:8px;color:#c8dff0;font-size:13px;padding:10px 14px}
|
|
30
|
+
.sf-footer{display:flex;justify-content:flex-end}
|
|
31
|
+
.sf-cancel{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:8px 20px;cursor:pointer}
|
|
32
|
+
`]
|
|
33
|
+
})
|
|
34
|
+
export class SearchDataComponent {
|
|
35
|
+
@Input() show = false;
|
|
36
|
+
@Output() close = new EventEmitter<void>();
|
|
37
|
+
query = '';
|
|
38
|
+
results = signal<string[]>([]);
|
|
39
|
+
search() { if (this.query.trim()) this.results.set(['No results found for "' + this.query + '"']); }
|
|
40
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
interface Egg { id: string; name: string; rarity: string; hatchProgress: number; hatched: boolean; reward: string }
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'oasis-eggs',
|
|
8
|
+
standalone: true,
|
|
9
|
+
imports: [CommonModule],
|
|
10
|
+
template: `
|
|
11
|
+
<div class="eggs-shell">
|
|
12
|
+
<div class="eggs-header">
|
|
13
|
+
<h2 class="eggs-title">🥚 Eggs</h2>
|
|
14
|
+
<p class="eggs-sub">Hatch eggs to discover rare OASIS companions and rewards.</p>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="eggs-grid">
|
|
17
|
+
@for (e of eggs(); track e.id) {
|
|
18
|
+
<div class="egg-card" [class.egg-card--hatched]="e.hatched">
|
|
19
|
+
<div class="egg-rarity egg-rarity--{{ e.rarity.toLowerCase() }}">{{ e.rarity }}</div>
|
|
20
|
+
<div class="egg-icon">{{ e.hatched ? '🐣' : '🥚' }}</div>
|
|
21
|
+
<div class="egg-name">{{ e.name }}</div>
|
|
22
|
+
@if (!e.hatched) {
|
|
23
|
+
<div class="egg-progress-wrap">
|
|
24
|
+
<div class="egg-progress-bar"><div class="egg-progress-fill" [style.width.%]="e.hatchProgress"></div></div>
|
|
25
|
+
<div class="egg-pct">{{ e.hatchProgress }}%</div>
|
|
26
|
+
</div>
|
|
27
|
+
<button class="egg-btn" (click)="incubate(e)">Incubate</button>
|
|
28
|
+
} @else {
|
|
29
|
+
<div class="egg-reward">✨ {{ e.reward }}</div>
|
|
30
|
+
}
|
|
31
|
+
</div>
|
|
32
|
+
}
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
`,
|
|
36
|
+
styles: [`
|
|
37
|
+
.eggs-shell{display:flex;flex-direction:column;gap:20px}
|
|
38
|
+
.eggs-header{text-align:center}
|
|
39
|
+
.eggs-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}
|
|
40
|
+
.eggs-sub{font-size:13px;color:#7a9bbf;margin:0}
|
|
41
|
+
.eggs-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:16px}
|
|
42
|
+
.egg-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.15);border-radius:12px;padding:18px 14px;display:flex;flex-direction:column;align-items:center;gap:10px;transition:border-color .2s}
|
|
43
|
+
.egg-card:hover{border-color:rgba(0,200,255,.35)}
|
|
44
|
+
.egg-card--hatched{border-color:rgba(255,180,60,.3);background:rgba(255,180,60,.04)}
|
|
45
|
+
.egg-rarity{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;border-radius:999px;padding:3px 10px;border:1px solid currentColor}
|
|
46
|
+
.egg-rarity--common{color:#7a9bbf;border-color:rgba(122,155,191,.3)}
|
|
47
|
+
.egg-rarity--rare{color:#5ba8ff;border-color:rgba(91,168,255,.3)}
|
|
48
|
+
.egg-rarity--epic{color:#b87fff;border-color:rgba(184,127,255,.3)}
|
|
49
|
+
.egg-rarity--legendary{color:#ffb43c;border-color:rgba(255,180,60,.3)}
|
|
50
|
+
.egg-icon{font-size:40px}
|
|
51
|
+
.egg-name{font-family:'Orbitron',sans-serif;font-size:12px;color:#fff;text-align:center}
|
|
52
|
+
.egg-progress-wrap{width:100%;display:flex;flex-direction:column;gap:4px;align-items:center}
|
|
53
|
+
.egg-progress-bar{width:100%;height:6px;background:rgba(255,255,255,.1);border-radius:999px;overflow:hidden}
|
|
54
|
+
.egg-progress-fill{height:100%;background:linear-gradient(90deg,#00c8ff,#0080ff);border-radius:999px;transition:width .4s}
|
|
55
|
+
.egg-pct{font-size:11px;color:#7a9bbf}
|
|
56
|
+
.egg-btn{width:100%;background:linear-gradient(135deg,#ffb43c,#ff8c00);border:none;border-radius:7px;color:#fff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;letter-spacing:.08em;padding:8px;cursor:pointer;transition:opacity .2s}
|
|
57
|
+
.egg-btn:hover{opacity:.85}
|
|
58
|
+
.egg-reward{font-size:12px;color:#ffb43c;font-weight:600;text-align:center}
|
|
59
|
+
`]
|
|
60
|
+
})
|
|
61
|
+
export class EggsComponent {
|
|
62
|
+
eggs = signal<Egg[]>([
|
|
63
|
+
{ id: '1', name: 'Cosmic Egg', rarity: 'Legendary', hatchProgress: 75, hatched: false, reward: '+500 Karma Companion' },
|
|
64
|
+
{ id: '2', name: 'Forest Egg', rarity: 'Common', hatchProgress: 100, hatched: true, reward: 'Woodland Sprite' },
|
|
65
|
+
{ id: '3', name: 'Astral Egg', rarity: 'Epic', hatchProgress: 30, hatched: false, reward: '+200 Karma Companion' },
|
|
66
|
+
{ id: '4', name: 'Ocean Egg', rarity: 'Rare', hatchProgress: 55, hatched: false, reward: 'Sea Guardian' },
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
incubate(e: Egg) {
|
|
70
|
+
this.eggs.update(list => list.map(x => {
|
|
71
|
+
if (x.id !== e.id) return x;
|
|
72
|
+
const next = Math.min(100, x.hatchProgress + 25);
|
|
73
|
+
return { ...x, hatchProgress: next, hatched: next >= 100 };
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'oasis-manage-eggs',
|
|
7
|
+
standalone: true,
|
|
8
|
+
imports: [CommonModule, FormsModule],
|
|
9
|
+
template: `
|
|
10
|
+
<div class="mf-shell">
|
|
11
|
+
<div class="mf-header"><span>🥚</span><h2 class="mf-title">Manage Eggs</h2></div>
|
|
12
|
+
<div class="mf-body">
|
|
13
|
+
<div class="mf-field"><label class="mf-lbl">Egg Name</label><input class="mf-inp" [(ngModel)]="form['eggname']" placeholder="Egg Name..." /></div>
|
|
14
|
+
<div class="mf-field"><label class="mf-lbl">Rarity</label><input class="mf-inp" [(ngModel)]="form['rarity']" placeholder="Rarity..." /></div>
|
|
15
|
+
<div class="mf-field"><label class="mf-lbl">Hatch Progress</label><input class="mf-inp" [(ngModel)]="form['hatchprogress']" placeholder="Hatch Progress..." /></div>
|
|
16
|
+
<div class="mf-field"><label class="mf-lbl">Reward</label><input class="mf-inp" [(ngModel)]="form['reward']" placeholder="Reward..." /></div>
|
|
17
|
+
<div class="mf-field"><label class="mf-lbl">Notes</label><input class="mf-inp" [(ngModel)]="form['notes']" placeholder="Notes..." /></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="mf-footer">
|
|
20
|
+
<button class="mf-save" (click)="save()">Save</button>
|
|
21
|
+
<button class="mf-cancel" (click)="close.emit()">Cancel</button>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
styles: [`
|
|
26
|
+
.mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
|
|
27
|
+
.mf-header{display:flex;align-items:center;gap:10px}
|
|
28
|
+
.mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
29
|
+
.mf-body{display:flex;flex-direction:column;gap:12px}
|
|
30
|
+
.mf-field{display:flex;flex-direction:column;gap:5px}
|
|
31
|
+
.mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
|
|
32
|
+
.mf-inp{background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:8px;color:#fff;font-size:13px;padding:10px 14px;outline:none}
|
|
33
|
+
.mf-inp:focus{border-color:rgba(0,200,255,.6)}
|
|
34
|
+
.mf-footer{display:flex;gap:10px;justify-content:flex-end}
|
|
35
|
+
.mf-save{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;padding:10px 22px;cursor:pointer}
|
|
36
|
+
.mf-cancel{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:10px 18px;cursor:pointer}
|
|
37
|
+
`]
|
|
38
|
+
})
|
|
39
|
+
export class ManageEggsComponent {
|
|
40
|
+
@Input() show = false;
|
|
41
|
+
@Output() close = new EventEmitter<void>();
|
|
42
|
+
form: Record<string,string> = {
|
|
43
|
+
'eggname': '',
|
|
44
|
+
'rarity': '',
|
|
45
|
+
'hatchprogress': '',
|
|
46
|
+
'reward': '',
|
|
47
|
+
'notes': ''
|
|
48
|
+
};
|
|
49
|
+
save() { this.close.emit(); }
|
|
50
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'oasis-search-eggs',
|
|
7
|
+
standalone: true,
|
|
8
|
+
imports: [CommonModule, FormsModule],
|
|
9
|
+
template: `
|
|
10
|
+
<div class="sf-shell">
|
|
11
|
+
<div class="sf-header"><span class="sf-icon">🔍</span><h2 class="sf-title">Search Eggs</h2></div>
|
|
12
|
+
<div class="sf-row">
|
|
13
|
+
<input class="sf-input" [(ngModel)]="query" placeholder="Search eggs by name or rarity..." (keyup.enter)="search()" />
|
|
14
|
+
<button class="sf-btn" (click)="search()">Search</button>
|
|
15
|
+
</div>
|
|
16
|
+
@for (r of results(); track r) { <div class="sf-result">{{ r }}</div> }
|
|
17
|
+
<div class="sf-footer"><button class="sf-cancel" (click)="close.emit()">Close</button></div>
|
|
18
|
+
</div>
|
|
19
|
+
`,
|
|
20
|
+
styles: [`
|
|
21
|
+
.sf-shell{display:flex;flex-direction:column;gap:14px;padding:20px}
|
|
22
|
+
.sf-header{display:flex;align-items:center;gap:10px}
|
|
23
|
+
.sf-icon{font-size:24px}
|
|
24
|
+
.sf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
25
|
+
.sf-row{display:flex;gap:10px}
|
|
26
|
+
.sf-input{flex:1;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:8px;color:#fff;font-size:13px;padding:10px 14px;outline:none}
|
|
27
|
+
.sf-input:focus{border-color:rgba(0,200,255,.6)}
|
|
28
|
+
.sf-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;padding:10px 20px;cursor:pointer;white-space:nowrap}
|
|
29
|
+
.sf-result{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:8px;color:#c8dff0;font-size:13px;padding:10px 14px}
|
|
30
|
+
.sf-footer{display:flex;justify-content:flex-end}
|
|
31
|
+
.sf-cancel{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:8px 20px;cursor:pointer}
|
|
32
|
+
`]
|
|
33
|
+
})
|
|
34
|
+
export class SearchEggsComponent {
|
|
35
|
+
@Input() show = false;
|
|
36
|
+
@Output() close = new EventEmitter<void>();
|
|
37
|
+
query = '';
|
|
38
|
+
results = signal<string[]>([]);
|
|
39
|
+
search() { if (this.query.trim()) this.results.set(['No results found for "' + this.query + '"']); }
|
|
40
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'oasis-view-eggs',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
template: `
|
|
9
|
+
<div class="ve-shell">
|
|
10
|
+
<div class="ve-header"><span>🥚</span><h2 class="ve-title">View Eggs</h2></div>
|
|
11
|
+
<div class="ve-grid">
|
|
12
|
+
@for (e of eggs(); track e.name) {
|
|
13
|
+
<div class="ve-card" [class.ve-card--hatched]="e.hatched">
|
|
14
|
+
<div class="ve-rarity ve-rarity--{{ e.rarity.toLowerCase() }}">{{ e.rarity }}</div>
|
|
15
|
+
<div class="ve-emoji">{{ e.hatched ? '🐣' : '🥚' }}</div>
|
|
16
|
+
<div class="ve-name">{{ e.name }}</div>
|
|
17
|
+
<div class="ve-pct">{{ e.progress }}%</div>
|
|
18
|
+
</div>
|
|
19
|
+
}
|
|
20
|
+
</div>
|
|
21
|
+
<div class="ve-footer"><button class="ve-btn" (click)="close.emit()">Close</button></div>
|
|
22
|
+
</div>
|
|
23
|
+
`,
|
|
24
|
+
styles: [`
|
|
25
|
+
.ve-shell{display:flex;flex-direction:column;gap:16px;padding:20px}
|
|
26
|
+
.ve-header{display:flex;align-items:center;gap:10px}
|
|
27
|
+
.ve-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
28
|
+
.ve-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:12px}
|
|
29
|
+
.ve-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.15);border-radius:10px;padding:14px;display:flex;flex-direction:column;align-items:center;gap:8px}
|
|
30
|
+
.ve-card--hatched{border-color:rgba(255,180,60,.3);background:rgba(255,180,60,.04)}
|
|
31
|
+
.ve-rarity{font-size:9px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;border-radius:999px;padding:2px 8px;border:1px solid currentColor}
|
|
32
|
+
.ve-rarity--common{color:#7a9bbf}.ve-rarity--rare{color:#5ba8ff}.ve-rarity--epic{color:#b87fff}.ve-rarity--legendary{color:#ffb43c}
|
|
33
|
+
.ve-emoji{font-size:32px}
|
|
34
|
+
.ve-name{font-family:'Orbitron',sans-serif;font-size:11px;color:#fff;text-align:center}
|
|
35
|
+
.ve-pct{font-size:11px;color:#7a9bbf}
|
|
36
|
+
.ve-footer{display:flex;justify-content:flex-end}
|
|
37
|
+
.ve-btn{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:9px 22px;cursor:pointer}
|
|
38
|
+
`]
|
|
39
|
+
})
|
|
40
|
+
export class ViewEggsComponent {
|
|
41
|
+
@Input() show = false;
|
|
42
|
+
@Output() close = new EventEmitter<void>();
|
|
43
|
+
eggs = signal([
|
|
44
|
+
{ name: 'Cosmic Egg', rarity: 'Legendary', progress: 75, hatched: false },
|
|
45
|
+
{ name: 'Forest Egg', rarity: 'Common', progress: 100, hatched: true },
|
|
46
|
+
{ name: 'Astral Egg', rarity: 'Epic', progress: 30, hatched: false },
|
|
47
|
+
{ name: 'Ocean Egg', rarity: 'Rare', progress: 55, hatched: false },
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Component, signal, Output, EventEmitter, inject } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
import { OasisService } from '../../services/oasis.service';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'oasis-forgot-password',
|
|
8
|
+
standalone: true,
|
|
9
|
+
imports: [CommonModule, FormsModule],
|
|
10
|
+
template: `
|
|
11
|
+
<div class="oasis-auth-form">
|
|
12
|
+
<div class="oasis-auth-header">
|
|
13
|
+
<h2 class="oasis-auth-title">Forgot Password?</h2>
|
|
14
|
+
<p class="oasis-auth-sub">Enter your email and we'll send a reset link if the account exists.</p>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
@if (error()) {
|
|
18
|
+
<div class="oasis-auth-error">{{ error() }}</div>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@if (!sent()) {
|
|
22
|
+
<div class="oasis-field">
|
|
23
|
+
<label class="oasis-label" for="fp-email">Email</label>
|
|
24
|
+
<input class="oasis-input" id="fp-email" type="email" [(ngModel)]="email" placeholder="name@example.com" autocomplete="email" required />
|
|
25
|
+
</div>
|
|
26
|
+
<button class="oasis-btn" type="button" [disabled]="loading()" (click)="submit()">
|
|
27
|
+
{{ loading() ? 'Sending…' : 'Send Reset Link' }}
|
|
28
|
+
</button>
|
|
29
|
+
} @else {
|
|
30
|
+
<div class="oasis-auth-success">
|
|
31
|
+
✅ Reset link sent! Check your inbox.<br>
|
|
32
|
+
<a class="oasis-link" (click)="switchTo.emit('login')" href="javascript:void(0)" style="margin-top:8px;display:inline-block">Back to Login</a>
|
|
33
|
+
</div>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
<a class="oasis-link oasis-link--small oasis-link--center" (click)="switchTo.emit('login')" href="javascript:void(0)">← Back to Login</a>
|
|
37
|
+
</div>
|
|
38
|
+
`,
|
|
39
|
+
styles: [`
|
|
40
|
+
.oasis-auth-form { display:flex; flex-direction:column; gap:20px; }
|
|
41
|
+
.oasis-auth-header { text-align:center; }
|
|
42
|
+
.oasis-auth-title { font-family:'Orbitron',sans-serif; font-size:20px; color:#fff; margin:0 0 6px; }
|
|
43
|
+
.oasis-auth-sub { font-size:13px; color:#7a9bbf; margin:0; line-height:1.5; }
|
|
44
|
+
.oasis-auth-error { background:rgba(255,80,80,.12); border:1px solid rgba(255,80,80,.3); color:#ff6b6b; border-radius:8px; padding:10px 14px; font-size:13px; }
|
|
45
|
+
.oasis-auth-success { background:rgba(72,220,130,.12); border:1px solid rgba(72,220,130,.3); color:#48dc82; border-radius:8px; padding:14px; font-size:13px; line-height:1.6; }
|
|
46
|
+
.oasis-field { display:flex; flex-direction:column; gap:6px; }
|
|
47
|
+
.oasis-label { font-size:12px; font-weight:600; letter-spacing:.06em; color:#7a9bbf; text-transform:uppercase; }
|
|
48
|
+
.oasis-input { width:100%; background:rgba(255,255,255,.05); border:1px solid rgba(0,200,255,.2); border-radius:8px; padding:10px 14px; color:#fff; font-size:14px; outline:none; box-sizing:border-box; transition:border-color .2s; }
|
|
49
|
+
.oasis-input:focus { border-color:rgba(0,200,255,.5); }
|
|
50
|
+
.oasis-link { color:#00c8ff; text-decoration:none; cursor:pointer; }
|
|
51
|
+
.oasis-link--small { font-size:12px; }
|
|
52
|
+
.oasis-link--center { text-align:center; }
|
|
53
|
+
.oasis-btn { background:linear-gradient(135deg,#00c8ff,#0080ff); border:none; border-radius:8px; color:#fff; font-family:'Orbitron',sans-serif; font-size:13px; font-weight:700; letter-spacing:.08em; padding:12px; cursor:pointer; transition:opacity .2s; }
|
|
54
|
+
.oasis-btn:disabled { opacity:.5; cursor:not-allowed; }
|
|
55
|
+
`]
|
|
56
|
+
})
|
|
57
|
+
export class ForgotPasswordComponent {
|
|
58
|
+
@Output() switchTo = new EventEmitter<'login'>();
|
|
59
|
+
|
|
60
|
+
private oasis = inject(OasisService);
|
|
61
|
+
|
|
62
|
+
email = '';
|
|
63
|
+
loading = signal(false);
|
|
64
|
+
error = signal('');
|
|
65
|
+
sent = signal(false);
|
|
66
|
+
|
|
67
|
+
async submit() {
|
|
68
|
+
if (!this.email) { this.error.set('Please enter your email address.'); return; }
|
|
69
|
+
this.loading.set(true);
|
|
70
|
+
this.error.set('');
|
|
71
|
+
try {
|
|
72
|
+
await this.oasis.forgotPassword(this.email);
|
|
73
|
+
this.sent.set(true);
|
|
74
|
+
} catch (e: any) {
|
|
75
|
+
this.error.set(e?.message ?? 'Request failed. Please try again.');
|
|
76
|
+
} finally {
|
|
77
|
+
this.loading.set(false);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
interface Game { id: string; name: string; genre: string; description: string; players: number; karma: number; icon: string; playing: boolean }
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'oasis-game',
|
|
8
|
+
standalone: true,
|
|
9
|
+
imports: [CommonModule],
|
|
10
|
+
template: `
|
|
11
|
+
<div class="game-shell">
|
|
12
|
+
<div class="game-header"><h2 class="game-title">🎮 Games</h2><p class="game-sub">Play games in the OASIS to earn karma and rewards.</p></div>
|
|
13
|
+
<div class="game-grid">
|
|
14
|
+
@for (g of games(); track g.id) {
|
|
15
|
+
<div class="game-card" [class.game-card--playing]="g.playing">
|
|
16
|
+
<div class="game-icon">{{ g.icon }}</div>
|
|
17
|
+
<div class="game-genre">{{ g.genre }}</div>
|
|
18
|
+
<div class="game-name">{{ g.name }}</div>
|
|
19
|
+
<div class="game-desc">{{ g.description }}</div>
|
|
20
|
+
<div class="game-meta">
|
|
21
|
+
<div class="game-players">👥 {{ g.players.toLocaleString() }}</div>
|
|
22
|
+
<div class="game-karma">+{{ g.karma }} karma/hr</div>
|
|
23
|
+
</div>
|
|
24
|
+
<button class="game-btn" [class.game-btn--stop]="g.playing" (click)="toggle(g)">{{ g.playing ? '⏹ Stop' : '▶ Play' }}</button>
|
|
25
|
+
</div>
|
|
26
|
+
}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
`,
|
|
30
|
+
styles: [`
|
|
31
|
+
.game-shell{display:flex;flex-direction:column;gap:18px}
|
|
32
|
+
.game-header{text-align:center}
|
|
33
|
+
.game-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}
|
|
34
|
+
.game-sub{font-size:13px;color:#7a9bbf;margin:0}
|
|
35
|
+
.game-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px}
|
|
36
|
+
.game-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:12px;padding:18px 14px;display:flex;flex-direction:column;gap:8px;transition:border-color .2s}
|
|
37
|
+
.game-card:hover{border-color:rgba(0,200,255,.3)}
|
|
38
|
+
.game-card--playing{border-color:rgba(0,200,255,.5);background:rgba(0,200,255,.05)}
|
|
39
|
+
.game-icon{font-size:40px}
|
|
40
|
+
.game-genre{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:#5ba8ff}
|
|
41
|
+
.game-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff}
|
|
42
|
+
.game-desc{font-size:12px;color:#7a9bbf;line-height:1.5;flex:1}
|
|
43
|
+
.game-meta{display:flex;justify-content:space-between}
|
|
44
|
+
.game-players{font-size:11px;color:#7a9bbf}
|
|
45
|
+
.game-karma{font-size:11px;color:#48dc82;font-weight:600}
|
|
46
|
+
.game-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:9px;cursor:pointer;transition:opacity .2s;margin-top:4px}
|
|
47
|
+
.game-btn--stop{background:rgba(255,80,80,.2);border:1px solid rgba(255,80,80,.35);color:#ff6b6b}
|
|
48
|
+
`]
|
|
49
|
+
})
|
|
50
|
+
export class GameComponent {
|
|
51
|
+
games = signal<Game[]>([
|
|
52
|
+
{ id: '1', name: 'Karma Wars', genre: 'Strategy', description: 'Battle other avatars for karma dominance in real-time.', players: 12450, karma: 50, icon: '⚔️', playing: false },
|
|
53
|
+
{ id: '2', name: 'Seed Garden', genre: 'Simulation', description: 'Grow and cultivate a virtual garden to earn seeds and karma.', players: 8900, karma: 30, icon: '🌱', playing: false },
|
|
54
|
+
{ id: '3', name: 'OASIS Racer', genre: 'Racing', description: 'Race across virtual worlds and earn speed karma.', players: 5600, karma: 40, icon: '🏎️', playing: false },
|
|
55
|
+
{ id: '4', name: 'NFT Quest', genre: 'RPG', description: 'An epic RPG adventure to discover legendary NFTs.', players: 22000, karma: 80, icon: '🗡️', playing: false },
|
|
56
|
+
]);
|
|
57
|
+
toggle(g: Game) { this.games.update(l => l.map(x => x.id === g.id ? { ...x, playing: !x.playing } : x)); }
|
|
58
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
import { FormsModule } from '@angular/forms';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'oasis-search-profiles',
|
|
7
|
+
standalone: true,
|
|
8
|
+
imports: [CommonModule, FormsModule],
|
|
9
|
+
template: `
|
|
10
|
+
<div class="sf-shell">
|
|
11
|
+
<div class="sf-header"><span>🎮</span><h2 class="sf-title">Search Profiles</h2></div>
|
|
12
|
+
<div class="sf-row"><input class="sf-input" [(ngModel)]="query" placeholder="Search by avatar name or game..." (keyup.enter)="search()" /><button class="sf-btn" (click)="search()">Search</button></div>
|
|
13
|
+
@for (r of results(); track r) { <div class="sf-result">{{ r }}</div> }
|
|
14
|
+
<div class="sf-footer"><button class="sf-cancel" (click)="close.emit()">Close</button></div>
|
|
15
|
+
</div>
|
|
16
|
+
`,
|
|
17
|
+
styles: [`
|
|
18
|
+
.sf-shell{display:flex;flex-direction:column;gap:14px;padding:20px}
|
|
19
|
+
.sf-header{display:flex;align-items:center;gap:10px}
|
|
20
|
+
.sf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
21
|
+
.sf-row{display:flex;gap:10px}
|
|
22
|
+
.sf-input{flex:1;background:rgba(255,255,255,.05);border:1px solid rgba(0,200,255,.25);border-radius:8px;color:#fff;font-size:13px;padding:10px 14px;outline:none}
|
|
23
|
+
.sf-input:focus{border-color:rgba(0,200,255,.6)}
|
|
24
|
+
.sf-btn{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;padding:10px 20px;cursor:pointer;white-space:nowrap}
|
|
25
|
+
.sf-result{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:8px;color:#c8dff0;font-size:13px;padding:10px 14px}
|
|
26
|
+
.sf-footer{display:flex;justify-content:flex-end}
|
|
27
|
+
.sf-cancel{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:8px 20px;cursor:pointer}
|
|
28
|
+
`]
|
|
29
|
+
})
|
|
30
|
+
export class SearchProfilesComponent {
|
|
31
|
+
@Input() show = false;
|
|
32
|
+
@Output() close = new EventEmitter<void>();
|
|
33
|
+
query = '';
|
|
34
|
+
results = signal<string[]>([]);
|
|
35
|
+
search() { if (this.query.trim()) this.results.set(['No results found for "' + this.query + '"']); }
|
|
36
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, signal } from '@angular/core';
|
|
2
|
+
import { CommonModule } from '@angular/common';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'oasis-view-achievements',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [CommonModule],
|
|
8
|
+
template: `
|
|
9
|
+
<div class="ach-shell">
|
|
10
|
+
<div class="ach-header"><span>🏆</span><h2 class="ach-title">Achievements</h2></div>
|
|
11
|
+
<div class="ach-list">
|
|
12
|
+
@for (a of achievements(); track a.id) {
|
|
13
|
+
<div class="ach-row" [class.ach-row--unlocked]="a.unlocked">
|
|
14
|
+
<div class="ach-badge">{{ a.unlocked ? a.icon : '🔒' }}</div>
|
|
15
|
+
<div class="ach-info"><div class="ach-name">{{ a.name }}</div><div class="ach-desc">{{ a.description }}</div></div>
|
|
16
|
+
<div class="ach-xp">+{{ a.xp }} XP</div>
|
|
17
|
+
</div>
|
|
18
|
+
}
|
|
19
|
+
</div>
|
|
20
|
+
<div class="ach-footer"><button class="ach-btn" (click)="close.emit()">Close</button></div>
|
|
21
|
+
</div>
|
|
22
|
+
`,
|
|
23
|
+
styles: [`
|
|
24
|
+
.ach-shell{display:flex;flex-direction:column;gap:16px;padding:20px}
|
|
25
|
+
.ach-header{display:flex;align-items:center;gap:10px}
|
|
26
|
+
.ach-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
|
|
27
|
+
.ach-list{display:flex;flex-direction:column;gap:8px}
|
|
28
|
+
.ach-row{display:flex;align-items:center;gap:14px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:12px 16px;opacity:.5;transition:opacity .2s}
|
|
29
|
+
.ach-row--unlocked{opacity:1;border-color:rgba(0,200,255,.2)}
|
|
30
|
+
.ach-badge{font-size:28px;flex-shrink:0}
|
|
31
|
+
.ach-info{flex:1}
|
|
32
|
+
.ach-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff}
|
|
33
|
+
.ach-desc{font-size:12px;color:#7a9bbf;margin-top:2px}
|
|
34
|
+
.ach-xp{font-size:12px;color:#ffb43c;font-weight:700;flex-shrink:0}
|
|
35
|
+
.ach-footer{display:flex;justify-content:flex-end}
|
|
36
|
+
.ach-btn{background:transparent;border:1px solid rgba(0,200,255,.3);border-radius:8px;color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:9px 22px;cursor:pointer}
|
|
37
|
+
`]
|
|
38
|
+
})
|
|
39
|
+
export class ViewAchievementsComponent {
|
|
40
|
+
@Input() show = false;
|
|
41
|
+
@Output() close = new EventEmitter<void>();
|
|
42
|
+
achievements = signal([
|
|
43
|
+
{ id:1, icon:'⚔️', name:'First Quest', description:'Complete your first quest', xp:100, unlocked:true },
|
|
44
|
+
{ id:2, icon:'🌟', name:'Star Collector', description:'Collect 100 stars', xp:250, unlocked:true },
|
|
45
|
+
{ id:3, icon:'🏛️', name:'Hub Master', description:'Visit all OASIS hubs', xp:500, unlocked:false },
|
|
46
|
+
{ id:4, icon:'🐉', name:'Dragon Slayer', description:'Defeat the cosmic dragon', xp:1000, unlocked:false },
|
|
47
|
+
]);
|
|
48
|
+
}
|