@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.
Files changed (118) hide show
  1. package/package.json +37 -0
  2. package/src/components/avatar/avatar-wallet.component.ts +49 -0
  3. package/src/components/avatar/edit-avatar.component.ts +28 -0
  4. package/src/components/avatar/search-avatar.component.ts +45 -0
  5. package/src/components/avatar/view-avatar.component.ts +49 -0
  6. package/src/components/avatar-connect/avatar-connect.component.ts +78 -0
  7. package/src/components/avatar-profile/avatar-profile.component.ts +146 -0
  8. package/src/components/coming-soon/coming-soon.component.ts +27 -0
  9. package/src/components/confirmation/confirmation.component.ts +38 -0
  10. package/src/components/contact/contact.component.ts +64 -0
  11. package/src/components/data-screen/add-data.component.ts +50 -0
  12. package/src/components/data-screen/cross-chain-management.component.ts +28 -0
  13. package/src/components/data-screen/load-data.component.ts +28 -0
  14. package/src/components/data-screen/manage-data.component.ts +50 -0
  15. package/src/components/data-screen/off-chain-management.component.ts +28 -0
  16. package/src/components/data-screen/search-data.component.ts +40 -0
  17. package/src/components/eggs/eggs.component.ts +76 -0
  18. package/src/components/eggs/manage-eggs.component.ts +50 -0
  19. package/src/components/eggs/search-eggs.component.ts +40 -0
  20. package/src/components/eggs/view-eggs.component.ts +49 -0
  21. package/src/components/forgot-password/forgot-password.component.ts +80 -0
  22. package/src/components/game/game.component.ts +58 -0
  23. package/src/components/game/search-profiles.component.ts +36 -0
  24. package/src/components/game/view-achievements.component.ts +48 -0
  25. package/src/components/game/view-leagues.component.ts +48 -0
  26. package/src/components/game/view-tournaments.component.ts +49 -0
  27. package/src/components/hyperdrive/hyperdrive.component.ts +81 -0
  28. package/src/components/karma/search-karma.component.ts +36 -0
  29. package/src/components/karma/view-avatar-karma.component.ts +51 -0
  30. package/src/components/karma/view-karma.component.ts +46 -0
  31. package/src/components/karma/vote-karma.component.ts +48 -0
  32. package/src/components/karma-panel/karma-panel.component.ts +87 -0
  33. package/src/components/karma-toast/karma-toast.component.ts +33 -0
  34. package/src/components/login/login.component.ts +95 -0
  35. package/src/components/map/add-2d-object-map.component.ts +42 -0
  36. package/src/components/map/add-3d-object-map.component.ts +43 -0
  37. package/src/components/map/add-quest-to-map.component.ts +42 -0
  38. package/src/components/map/download-our-world.component.ts +28 -0
  39. package/src/components/map/manage-map.component.ts +28 -0
  40. package/src/components/map/map.component.ts +70 -0
  41. package/src/components/map/plot-route-on-map.component.ts +42 -0
  42. package/src/components/map/search-map.component.ts +44 -0
  43. package/src/components/map/view-global-3d-map.component.ts +28 -0
  44. package/src/components/map/view-halons-on-map.component.ts +28 -0
  45. package/src/components/map/view-oapp-on-map.component.ts +28 -0
  46. package/src/components/map/view-quest-on-map.component.ts +28 -0
  47. package/src/components/menu-message/menu-message.component.ts +69 -0
  48. package/src/components/messages/message.component.ts +59 -0
  49. package/src/components/messaging/messaging.component.ts +89 -0
  50. package/src/components/mission/manage-mission.component.ts +43 -0
  51. package/src/components/mission/mission.component.ts +63 -0
  52. package/src/components/mission/search-mission.component.ts +36 -0
  53. package/src/components/mission/view-mission.component.ts +49 -0
  54. package/src/components/modal/modal.component.ts +28 -0
  55. package/src/components/navbar/navbar.component.ts +64 -0
  56. package/src/components/nft/contact-popup.component.ts +41 -0
  57. package/src/components/nft/manage-oasis-nft.component.ts +43 -0
  58. package/src/components/nft/nft.component.ts +70 -0
  59. package/src/components/nft/purchase-oasis-nft.component.ts +53 -0
  60. package/src/components/nft/purchase-oasis-virtual-land-nft.component.ts +28 -0
  61. package/src/components/nft/search-oasis-nft.component.ts +36 -0
  62. package/src/components/nft/solana.component.ts +28 -0
  63. package/src/components/nft/view-oasis-nft.component.ts +52 -0
  64. package/src/components/oapp/create-oapp.component.ts +43 -0
  65. package/src/components/oapp/deploy-oapp.component.ts +42 -0
  66. package/src/components/oapp/download-our-world.component.ts +28 -0
  67. package/src/components/oapp/edit-oapp.component.ts +41 -0
  68. package/src/components/oapp/install-oapp.component.ts +36 -0
  69. package/src/components/oapp/launch-oapp.component.ts +58 -0
  70. package/src/components/oapp/manage-oapp.component.ts +42 -0
  71. package/src/components/oapp/oapp.component.ts +70 -0
  72. package/src/components/oapp/search-oapp.component.ts +36 -0
  73. package/src/components/oasis-card/oasis-card.component.ts +30 -0
  74. package/src/components/onet/onet.component.ts +77 -0
  75. package/src/components/onode/onode.component.ts +73 -0
  76. package/src/components/provider/activity-pub.component.ts +44 -0
  77. package/src/components/provider/compare-provider-speeds.component.ts +48 -0
  78. package/src/components/provider/eosio.component.ts +44 -0
  79. package/src/components/provider/ethereum.component.ts +44 -0
  80. package/src/components/provider/holochain.component.ts +44 -0
  81. package/src/components/provider/ipfs.component.ts +44 -0
  82. package/src/components/provider/manage-auto-fail-over.component.ts +42 -0
  83. package/src/components/provider/manage-auto-replicaton.component.ts +42 -0
  84. package/src/components/provider/manage-load-balancing.component.ts +42 -0
  85. package/src/components/provider/manage-providers.component.ts +28 -0
  86. package/src/components/provider/mongo-db.component.ts +44 -0
  87. package/src/components/provider/neo4j.component.ts +44 -0
  88. package/src/components/provider/search-providers.component.ts +36 -0
  89. package/src/components/provider/seeds-provider.component.ts +42 -0
  90. package/src/components/provider/solid.component.ts +44 -0
  91. package/src/components/provider/sql-lite.component.ts +44 -0
  92. package/src/components/provider/three-fold.component.ts +44 -0
  93. package/src/components/provider/view-provider-stats.component.ts +61 -0
  94. package/src/components/provider/view-providers.component.ts +54 -0
  95. package/src/components/provider-dropdown/provider-dropdown.component.ts +64 -0
  96. package/src/components/providers/providers.component.ts +60 -0
  97. package/src/components/quest/manage-quest.component.ts +43 -0
  98. package/src/components/quest/quest.component.ts +65 -0
  99. package/src/components/quest/search-quest.component.ts +36 -0
  100. package/src/components/quest/view-quest.component.ts +49 -0
  101. package/src/components/reset-password/reset-password.component.ts +68 -0
  102. package/src/components/search-avatars/search-avatars.component.ts +80 -0
  103. package/src/components/seeds/accept-invite.component.ts +44 -0
  104. package/src/components/seeds/donate-seeds.component.ts +42 -0
  105. package/src/components/seeds/manage-seeds.component.ts +42 -0
  106. package/src/components/seeds/pay-with-seeds.component.ts +41 -0
  107. package/src/components/seeds/reward-seeds.component.ts +41 -0
  108. package/src/components/seeds/search-seeds.component.ts +36 -0
  109. package/src/components/seeds/seeds.component.ts +75 -0
  110. package/src/components/seeds/send-invite.component.ts +41 -0
  111. package/src/components/seeds/view-organizations.component.ts +55 -0
  112. package/src/components/seeds/view-seeds.component.ts +78 -0
  113. package/src/components/settings/settings.component.ts +96 -0
  114. package/src/components/signup/signup.component.ts +118 -0
  115. package/src/components/star-field/star-field.component.ts +44 -0
  116. package/src/components/verify-email/verify-email.component.ts +48 -0
  117. package/src/components/wallet/wallet.component.ts +83 -0
  118. package/src/index.ts +145 -0
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@oasisomniverse/angular",
3
+ "version": "1.0.0",
4
+ "description": "OASIS Omniverse Angular 17 standalone UI components",
5
+ "keywords": [
6
+ "oasis",
7
+ "omniverse",
8
+ "angular",
9
+ "components",
10
+ "standalone"
11
+ ],
12
+ "author": "NextGenSoftware",
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/NextGenSoftwareUK/OASIS-Angular-UI-Component-Library.git"
17
+ },
18
+ "main": "src/index.ts",
19
+ "exports": {
20
+ ".": "./src/index.ts",
21
+ "./components/*": "./src/components/*.component.ts"
22
+ },
23
+ "files": [
24
+ "src"
25
+ ],
26
+ "peerDependencies": {
27
+ "@angular/core": "\u003e=17.0.0",
28
+ "@angular/common": "\u003e=17.0.0",
29
+ "@oasisomniverse/web4-api": "\u003e=2.0.0"
30
+ },
31
+ "scripts": {
32
+
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ }
37
+ }
@@ -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-avatar-wallet',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="aw-shell">
10
+ <div class="aw-header"><span class="aw-icon">💰</span><h2 class="aw-title">Avatar Wallet</h2></div>
11
+ <div class="aw-table-wrap">
12
+ <table class="aw-table">
13
+ <thead><tr><th>Date</th><th>Note</th><th>From Avatar</th><th>Token</th><th>Amount</th><th>Balance</th><th>Provider</th><th>Type</th></tr></thead>
14
+ <tbody>
15
+ @for (row of transactions(); track row.id) {
16
+ <tr><td>{{ row.date }}</td><td>{{ row.note }}</td><td>{{ row.fromAvatar }}</td><td>{{ row.token }}</td><td>{{ row.amount }}</td><td>{{ row.balance }}</td><td>{{ row.provider }}</td><td><span class="aw-badge aw-badge--{{ row.type.toLowerCase() }}">{{ row.type }}</span></td></tr>
17
+ }
18
+ </tbody>
19
+ </table>
20
+ </div>
21
+ <div class="aw-footer"><button class="aw-btn" (click)="close.emit()">Close</button></div>
22
+ </div>
23
+ `,
24
+ styles: [`
25
+ .aw-shell{display:flex;flex-direction:column;gap:16px;padding:20px}
26
+ .aw-header{display:flex;align-items:center;gap:10px}
27
+ .aw-icon{font-size:26px}
28
+ .aw-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
29
+ .aw-table-wrap{overflow-x:auto;border:1px solid rgba(0,200,255,.15);border-radius:10px}
30
+ .aw-table{width:100%;border-collapse:collapse;font-size:13px}
31
+ .aw-table th{background:rgba(0,200,255,.08);color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:.06em;padding:10px 12px;text-align:left;border-bottom:1px solid rgba(0,200,255,.15);white-space:nowrap}
32
+ .aw-table td{color:#c8dff0;padding:9px 12px;border-bottom:1px solid rgba(255,255,255,.04)}
33
+ .aw-table tr:last-child td{border-bottom:none}
34
+ .aw-table tr:hover td{background:rgba(0,200,255,.04)}
35
+ .aw-badge{border-radius:999px;padding:2px 9px;font-size:10px;font-weight:700}
36
+ .aw-badge--credit{background:rgba(0,200,100,.15);color:#00c864;border:1px solid rgba(0,200,100,.3)}
37
+ .aw-badge--debit{background:rgba(255,80,80,.12);color:#ff5050;border:1px solid rgba(255,80,80,.3)}
38
+ .aw-footer{display:flex;justify-content:flex-end}
39
+ .aw-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}
40
+ `]
41
+ })
42
+ export class AvatarWalletComponent {
43
+ @Input() show = false;
44
+ @Output() close = new EventEmitter<void>();
45
+ transactions = signal([
46
+ { id: 1, date: '2026-07-01', note: 'Quest reward', fromAvatar: 'StarWalker', token: 'SEEDS', amount: '50', balance: '350', provider: 'Holochain', type: 'Credit' },
47
+ { id: 2, date: '2026-07-03', note: 'NFT purchase', fromAvatar: 'Self', token: 'ETH', amount: '0.05', balance: '1.12', provider: 'Ethereum', type: 'Debit' },
48
+ ]);
49
+ }
@@ -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-edit-avatar',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="popup-shell">
10
+ <div class="popup-icon">✏️</div>
11
+ <h2 class="popup-title">Edit Avatar</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 EditAvatarComponent {
26
+ @Input() show = false;
27
+ @Output() close = new EventEmitter<void>();
28
+ }
@@ -0,0 +1,45 @@
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-avatar',
7
+ standalone: true,
8
+ imports: [CommonModule, FormsModule],
9
+ template: `
10
+ <div class="sa-shell">
11
+ <div class="sa-header"><span>🔍</span><h2 class="sa-title">Search Avatars</h2></div>
12
+ <div class="sa-row">
13
+ <input class="sa-input" [(ngModel)]="query" placeholder="Username or avatar name..." (keyup.enter)="search()" />
14
+ <button class="sa-btn" (click)="search()">Search</button>
15
+ </div>
16
+ @for (r of results(); track r.name) {
17
+ <div class="sa-card"><div class="sa-name">{{ r.name }}</div><div class="sa-meta">Level {{ r.level }} · {{ r.karma }} karma</div></div>
18
+ }
19
+ <button class="sa-cancel" (click)="close.emit()">Close</button>
20
+ </div>
21
+ `,
22
+ styles: [`
23
+ .sa-shell{display:flex;flex-direction:column;gap:14px;padding:20px}
24
+ .sa-header{display:flex;align-items:center;gap:10px}
25
+ .sa-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
26
+ .sa-row{display:flex;gap:10px}
27
+ .sa-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}
28
+ .sa-input:focus{border-color:rgba(0,200,255,.6)}
29
+ .sa-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}
30
+ .sa-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:8px;padding:12px 16px;display:flex;justify-content:space-between;align-items:center}
31
+ .sa-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff}
32
+ .sa-meta{font-size:12px;color:#7a9bbf}
33
+ .sa-cancel{align-self:flex-end;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}
34
+ `]
35
+ })
36
+ export class SearchAvatarComponent {
37
+ @Input() show = false;
38
+ @Output() close = new EventEmitter<void>();
39
+ query = '';
40
+ results = signal<{name:string;level:number;karma:number}[]>([]);
41
+ search() {
42
+ if (!this.query.trim()) return;
43
+ this.results.set([{ name: this.query, level: 7, karma: 420 }]);
44
+ }
45
+ }
@@ -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-avatar',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="va-shell">
10
+ <div class="va-header"><span>👤</span><h2 class="va-title">View Avatars</h2></div>
11
+ <div class="va-table-wrap">
12
+ <table class="va-table">
13
+ <thead><tr><th>Avatar</th><th>Level</th><th>Karma</th><th>Sex</th><th>Created</th><th>Online</th></tr></thead>
14
+ <tbody>
15
+ @for (a of avatars(); track a.name) {
16
+ <tr>
17
+ <td>{{ a.name }}</td><td>{{ a.level }}</td><td>{{ a.karma }}</td><td>{{ a.sex }}</td><td>{{ a.created }}</td>
18
+ <td><span class="va-dot" [class.va-dot--on]="a.online">{{ a.online ? '🟢' : '⚪' }}</span></td>
19
+ </tr>
20
+ }
21
+ </tbody>
22
+ </table>
23
+ </div>
24
+ <div class="va-footer"><button class="va-btn" (click)="close.emit()">Close</button></div>
25
+ </div>
26
+ `,
27
+ styles: [`
28
+ .va-shell{display:flex;flex-direction:column;gap:16px;padding:20px}
29
+ .va-header{display:flex;align-items:center;gap:10px}
30
+ .va-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
31
+ .va-table-wrap{overflow-x:auto;border:1px solid rgba(0,200,255,.15);border-radius:10px}
32
+ .va-table{width:100%;border-collapse:collapse;font-size:13px}
33
+ .va-table th{background:rgba(0,200,255,.08);color:#00c8ff;font-family:'Orbitron',sans-serif;font-size:10px;font-weight:700;letter-spacing:.06em;padding:10px 12px;text-align:left;border-bottom:1px solid rgba(0,200,255,.15)}
34
+ .va-table td{color:#c8dff0;padding:9px 12px;border-bottom:1px solid rgba(255,255,255,.04)}
35
+ .va-table tr:last-child td{border-bottom:none}
36
+ .va-table tr:hover td{background:rgba(0,200,255,.04)}
37
+ .va-footer{display:flex;justify-content:flex-end}
38
+ .va-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}
39
+ `]
40
+ })
41
+ export class ViewAvatarComponent {
42
+ @Input() show = false;
43
+ @Output() close = new EventEmitter<void>();
44
+ avatars = signal([
45
+ { name: 'StarWalker', level: 12, karma: 840, sex: 'Male', created: '2024-01-15', online: true },
46
+ { name: 'NebulaDancer', level: 7, karma: 420, sex: 'Female', created: '2024-03-22', online: false },
47
+ { name: 'CosmicSage', level: 20, karma: 1500, sex: 'Male', created: '2023-11-01', online: true },
48
+ ]);
49
+ }
@@ -0,0 +1,78 @@
1
+ import { Component, signal, 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-avatar-connect',
8
+ standalone: true,
9
+ imports: [CommonModule, FormsModule],
10
+ template: `
11
+ @if (session()) {
12
+ <div class="avatar-badge">
13
+ <span class="avatar-icon">⬡</span>
14
+ <span class="avatar-name">{{ session()!.username }}</span>
15
+ <span class="karma-pill">{{ session()!.karma | number }} ✦</span>
16
+ <button class="logout-btn" (click)="oasis.logout()">✕</button>
17
+ </div>
18
+ } @else {
19
+ <button class="connect-btn" (click)="open = true">⬡ CONNECT AVATAR</button>
20
+ }
21
+
22
+ @if (open) {
23
+ <div class="modal-backdrop" (click)="open = false">
24
+ <div class="modal-box" (click)="$event.stopPropagation()">
25
+ <button class="modal-close" (click)="open = false">✕</button>
26
+ <div class="modal-icon">⬡</div>
27
+ <h2 class="modal-title">Connect Your Avatar</h2>
28
+ <p class="modal-sub">Sign in to your OASIS avatar to earn karma and participate in Global Healing Network.</p>
29
+ <input [(ngModel)]="username" placeholder="Avatar username" class="oasis-input" />
30
+ <input [(ngModel)]="password" type="password" placeholder="Password" class="oasis-input" />
31
+ @if (error) { <p class="error-msg">{{ error }}</p> }
32
+ <button class="btn-primary" [disabled]="loading" (click)="login()">
33
+ {{ loading ? 'Connecting…' : 'Connect Avatar' }}
34
+ </button>
35
+ </div>
36
+ </div>
37
+ }
38
+ `,
39
+ styles: [`
40
+ .connect-btn { background: none; border: 1px solid rgba(232,121,249,.4); border-radius: 999px; padding: 8px 18px; color: #e879f9; font-family: 'Share Tech Mono', monospace; font-size: 11px; letter-spacing: .1em; cursor: pointer; transition: background .2s; }
41
+ .connect-btn:hover { background: rgba(232,121,249,.08); }
42
+ .avatar-badge { display: flex; align-items: center; gap: 8px; }
43
+ .avatar-icon { color: #e879f9; font-size: 16px; }
44
+ .avatar-name { font-family: 'Share Tech Mono', monospace; font-size: 12px; color: #fff; letter-spacing: .06em; }
45
+ .karma-pill { background: rgba(232,121,249,.12); border: 1px solid rgba(232,121,249,.3); border-radius: 999px; padding: 2px 10px; font-family: 'Share Tech Mono', monospace; font-size: 11px; color: #e879f9; }
46
+ .logout-btn { background: none; border: none; color: #a8bfd8; cursor: pointer; font-size: 14px; margin-left: 4px; }
47
+ .modal-backdrop { position: fixed; inset: 0; z-index: 9000; background: rgba(3,7,20,.85); backdrop-filter: blur(8px); display: flex; align-items: center; justify-content: center; padding: 20px; }
48
+ .modal-box { background: #0a1535; border: 1px solid rgba(232,121,249,.25); border-radius: 20px; padding: 40px 36px; max-width: 440px; width: 100%; position: relative; }
49
+ .modal-close { position: absolute; top: 16px; right: 18px; background: none; border: none; color: #a8bfd8; font-size: 20px; cursor: pointer; }
50
+ .modal-icon { font-size: 40px; text-align: center; color: #e879f9; margin-bottom: 16px; }
51
+ .modal-title { font-family: 'Orbitron', sans-serif; font-size: 20px; font-weight: 700; color: #fff; text-align: center; margin: 0 0 8px; }
52
+ .modal-sub { font-size: 14px; color: #a8bfd8; text-align: center; margin: 0 0 24px; line-height: 1.6; }
53
+ .oasis-input { width: 100%; background: rgba(255,255,255,.05); border: 1px solid rgba(232,121,249,.2); border-radius: 8px; padding: 11px 14px; color: #fff; font-size: 14px; outline: none; box-sizing: border-box; margin-bottom: 12px; display: block; }
54
+ .btn-primary { width: 100%; padding: 14px; background: linear-gradient(135deg, #e879f9, #a855f7); border: none; border-radius: 10px; color: #fff; font-family: 'Orbitron', sans-serif; font-size: 13px; font-weight: 700; letter-spacing: .08em; cursor: pointer; margin-top: 4px; }
55
+ .btn-primary:disabled { opacity: .5; cursor: not-allowed; }
56
+ .error-msg { color: #f87171; font-size: 13px; text-align: center; margin-bottom: 8px; }
57
+ `]
58
+ })
59
+ export class AvatarConnectComponent {
60
+ oasis = inject(OasisService);
61
+ session = this.oasis.session;
62
+ open = false;
63
+ loading = false;
64
+ error = '';
65
+ username = '';
66
+ password = '';
67
+
68
+ async login(): Promise<void> {
69
+ if (!this.username || !this.password) { this.error = 'Please fill in both fields.'; return; }
70
+ this.loading = true; this.error = '';
71
+ try {
72
+ await this.oasis.login(this.username, this.password);
73
+ this.open = false; this.username = ''; this.password = '';
74
+ } catch {
75
+ this.error = 'Login failed. Please check your credentials.';
76
+ } finally { this.loading = false; }
77
+ }
78
+ }
@@ -0,0 +1,146 @@
1
+ import { Component, OnInit, signal, 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
+ interface AvatarFields {
7
+ title: string; firstName: string; lastName: string;
8
+ username: string; email: string; address: string;
9
+ }
10
+
11
+ @Component({
12
+ selector: 'oasis-avatar-profile',
13
+ standalone: true,
14
+ imports: [CommonModule, FormsModule],
15
+ template: `
16
+ <div class="avp-shell">
17
+ @if (!oasis.session()) {
18
+ <div class="avp-empty">Please <strong>Beam In</strong> to view your avatar.</div>
19
+ } @else {
20
+ <div class="avp-layout">
21
+ <aside class="avp-summary">
22
+ <div class="avp-avatar-img">
23
+ <span class="avp-avatar-icon">👤</span>
24
+ </div>
25
+ <div class="avp-name">{{ oasis.session()?.username }}</div>
26
+ <div class="avp-stats">
27
+ <div class="avp-stat">
28
+ <span class="avp-stat-icon">⚡</span>
29
+ <div>
30
+ <div class="avp-stat-value">{{ oasis.session()?.karma ?? '—' }}</div>
31
+ <div class="avp-stat-label">Karma</div>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </aside>
36
+
37
+ <div class="avp-fields">
38
+ <div class="avp-section-label">PROFILE</div>
39
+ <div class="avp-field-row">
40
+ <label class="avp-label" for="avp-title">Title</label>
41
+ <input class="avp-input" id="avp-title" type="text" [(ngModel)]="fields.title" placeholder="Dr, Mr, Ms…" (input)="dirty = true" />
42
+ </div>
43
+ <div class="avp-field-row">
44
+ <label class="avp-label" for="avp-first">First Name</label>
45
+ <input class="avp-input" id="avp-first" type="text" [(ngModel)]="fields.firstName" placeholder="First name" (input)="dirty = true" />
46
+ </div>
47
+ <div class="avp-field-row">
48
+ <label class="avp-label" for="avp-last">Last Name</label>
49
+ <input class="avp-input" id="avp-last" type="text" [(ngModel)]="fields.lastName" placeholder="Last name" (input)="dirty = true" />
50
+ </div>
51
+ <div class="avp-field-row">
52
+ <label class="avp-label" for="avp-username">Username</label>
53
+ <input class="avp-input" id="avp-username" type="text" [(ngModel)]="fields.username" placeholder="Username" (input)="dirty = true" />
54
+ </div>
55
+
56
+ <div class="avp-section-label" style="margin-top:18px">CONTACT</div>
57
+ <div class="avp-field-row">
58
+ <label class="avp-label" for="avp-email">Email</label>
59
+ <input class="avp-input" id="avp-email" type="email" [(ngModel)]="fields.email" placeholder="name@example.com" (input)="dirty = true" />
60
+ </div>
61
+ <div class="avp-field-row">
62
+ <label class="avp-label" for="avp-address">Address</label>
63
+ <input class="avp-input" id="avp-address" type="text" [(ngModel)]="fields.address" placeholder="Optional" (input)="dirty = true" />
64
+ </div>
65
+
66
+ @if (status()) {
67
+ <div class="avp-status" [class.avp-status--error]="statusType() === 'error'">{{ status() }}</div>
68
+ }
69
+
70
+ @if (dirty) {
71
+ <div class="avp-actions">
72
+ <button class="avp-btn" (click)="save()" [disabled]="saving()">{{ saving() ? 'Saving…' : 'Save Changes' }}</button>
73
+ <button class="avp-btn avp-btn--ghost" (click)="discard()">Discard</button>
74
+ </div>
75
+ }
76
+ </div>
77
+ </div>
78
+ }
79
+ </div>
80
+ `,
81
+ styles: [`
82
+ .avp-shell { color:#fff; }
83
+ .avp-empty { text-align:center; padding:40px; color:#7a9bbf; font-size:14px; }
84
+ .avp-layout { display:grid; grid-template-columns:180px 1fr; gap:28px; }
85
+ .avp-summary { display:flex; flex-direction:column; align-items:center; gap:14px; }
86
+ .avp-avatar-img { width:88px; height:88px; border-radius:50%; background:rgba(0,200,255,.1); border:2px solid rgba(0,200,255,.3); display:flex; align-items:center; justify-content:center; }
87
+ .avp-avatar-icon { font-size:40px; }
88
+ .avp-name { font-family:'Orbitron',sans-serif; font-size:13px; color:#fff; text-align:center; word-break:break-all; }
89
+ .avp-stats { display:flex; flex-direction:column; gap:8px; width:100%; }
90
+ .avp-stat { display:flex; align-items:center; gap:8px; background:rgba(0,200,255,.06); border-radius:8px; padding:8px 10px; }
91
+ .avp-stat-icon { font-size:16px; }
92
+ .avp-stat-value { font-family:'Orbitron',sans-serif; font-size:14px; color:#00c8ff; }
93
+ .avp-stat-label { font-size:11px; color:#7a9bbf; text-transform:uppercase; letter-spacing:.06em; }
94
+ .avp-fields { display:flex; flex-direction:column; gap:12px; }
95
+ .avp-section-label { font-size:10px; font-weight:700; letter-spacing:.12em; color:#7a9bbf; text-transform:uppercase; margin-bottom:4px; }
96
+ .avp-field-row { display:grid; grid-template-columns:90px 1fr; align-items:center; gap:12px; }
97
+ .avp-label { font-size:12px; color:#7a9bbf; text-align:right; }
98
+ .avp-input { width:100%; background:rgba(255,255,255,.05); border:1px solid rgba(0,200,255,.2); border-radius:6px; padding:8px 12px; color:#fff; font-size:13px; outline:none; box-sizing:border-box; transition:border-color .2s; }
99
+ .avp-input:focus { border-color:rgba(0,200,255,.5); }
100
+ .avp-status { margin-top:4px; font-size:13px; color:#48dc82; }
101
+ .avp-status--error { color:#ff6b6b; }
102
+ .avp-actions { display:flex; gap:10px; margin-top:4px; }
103
+ .avp-btn { background:linear-gradient(135deg,#00c8ff,#0080ff); border:none; border-radius:6px; color:#fff; font-family:'Orbitron',sans-serif; font-size:11px; font-weight:700; letter-spacing:.08em; padding:9px 16px; cursor:pointer; transition:opacity .2s; }
104
+ .avp-btn:disabled { opacity:.5; cursor:not-allowed; }
105
+ .avp-btn--ghost { background:transparent; border:1px solid rgba(0,200,255,.3); color:#00c8ff; }
106
+ `]
107
+ })
108
+ export class AvatarProfileComponent implements OnInit {
109
+ oasis = inject(OasisService);
110
+
111
+ fields: AvatarFields = { title: '', firstName: '', lastName: '', username: '', email: '', address: '' };
112
+ dirty = false;
113
+ saving = signal(false);
114
+ status = signal('');
115
+ statusType = signal<'ok' | 'error'>('ok');
116
+
117
+ ngOnInit() {
118
+ const sess = this.oasis.session();
119
+ if (sess) this.fields.username = sess.username;
120
+ }
121
+
122
+ async save() {
123
+ this.saving.set(true);
124
+ this.status.set('');
125
+ try {
126
+ // Avatar update would use oasis.avatar.updateAvatar() when available
127
+ await new Promise(r => setTimeout(r, 600));
128
+ this.dirty = false;
129
+ this.statusType.set('ok');
130
+ this.status.set('Changes saved.');
131
+ setTimeout(() => this.status.set(''), 3000);
132
+ } catch (e: any) {
133
+ this.statusType.set('error');
134
+ this.status.set(e?.message ?? 'Save failed.');
135
+ } finally {
136
+ this.saving.set(false);
137
+ }
138
+ }
139
+
140
+ discard() {
141
+ const sess = this.oasis.session();
142
+ this.fields = { title: '', firstName: '', lastName: '', username: sess?.username ?? '', email: '', address: '' };
143
+ this.dirty = false;
144
+ this.status.set('');
145
+ }
146
+ }
@@ -0,0 +1,27 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-coming-soon',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="cs-shell">
10
+ <div class="cs-icon">🚀</div>
11
+ <h2 class="cs-title">{{ title }}</h2>
12
+ <p class="cs-body">{{ message }}</p>
13
+ <div class="cs-badge">Coming Soon</div>
14
+ </div>
15
+ `,
16
+ styles: [`
17
+ .cs-shell{display:flex;flex-direction:column;align-items:center;gap:16px;padding:40px 20px;text-align:center}
18
+ .cs-icon{font-size:48px}
19
+ .cs-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0}
20
+ .cs-body{font-size:14px;color:#7a9bbf;line-height:1.6;margin:0;max-width:360px}
21
+ .cs-badge{background:rgba(255,180,60,.12);border:1px solid rgba(255,180,60,.28);color:#ffb43c;border-radius:999px;font-size:11px;font-weight:700;letter-spacing:.1em;padding:5px 14px;text-transform:uppercase}
22
+ `]
23
+ })
24
+ export class ComingSoonComponent {
25
+ @Input() title = 'Feature Coming Soon';
26
+ @Input() message = 'This feature is currently under development and will be available soon.';
27
+ }
@@ -0,0 +1,38 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-confirmation',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="conf-shell">
10
+ <div class="conf-icon">{{ icon }}</div>
11
+ <h2 class="conf-title">{{ title }}</h2>
12
+ <p class="conf-body">{{ message }}</p>
13
+ <div class="conf-actions">
14
+ <button class="conf-btn conf-btn--confirm" (click)="confirm.emit()">{{ confirmLabel }}</button>
15
+ <button class="conf-btn conf-btn--cancel" (click)="cancel.emit()">{{ cancelLabel }}</button>
16
+ </div>
17
+ </div>
18
+ `,
19
+ styles: [`
20
+ .conf-shell{display:flex;flex-direction:column;align-items:center;gap:18px;padding:20px;text-align:center}
21
+ .conf-icon{font-size:44px}
22
+ .conf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
23
+ .conf-body{font-size:14px;color:#a8bfd8;line-height:1.6;margin:0;max-width:360px}
24
+ .conf-actions{display:flex;gap:12px;margin-top:4px}
25
+ .conf-btn{border:none;border-radius:8px;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;letter-spacing:.08em;padding:11px 24px;cursor:pointer;transition:opacity .2s}
26
+ .conf-btn--confirm{background:linear-gradient(135deg,#ff4444,#cc0000);color:#fff}
27
+ .conf-btn--cancel{background:transparent;border:1px solid rgba(0,200,255,.3);color:#00c8ff}
28
+ `]
29
+ })
30
+ export class ConfirmationComponent {
31
+ @Input() title = 'Are you sure?';
32
+ @Input() message = 'This action cannot be undone.';
33
+ @Input() icon = '⚠️';
34
+ @Input() confirmLabel = 'Confirm';
35
+ @Input() cancelLabel = 'Cancel';
36
+ @Output() confirm = new EventEmitter<void>();
37
+ @Output() cancel = new EventEmitter<void>();
38
+ }
@@ -0,0 +1,64 @@
1
+ import { Component, signal } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+
5
+ @Component({
6
+ selector: 'oasis-contact',
7
+ standalone: true,
8
+ imports: [CommonModule, FormsModule],
9
+ template: `
10
+ <div class="ct-shell">
11
+ <div class="ct-header">
12
+ <h2 class="ct-title">Contact Us</h2>
13
+ <p class="ct-sub">Get in touch with the OASIS team.</p>
14
+ </div>
15
+ @if (sent()) {
16
+ <div class="oasis-auth-success">✅ Message sent! We'll be in touch soon.</div>
17
+ } @else {
18
+ @if (error()) { <div class="oasis-auth-error">{{ error() }}</div> }
19
+ <div class="oasis-grid-2">
20
+ <div class="oasis-field"><label class="oasis-label">First Name</label><input class="oasis-input" type="text" [(ngModel)]="firstName" placeholder="John" /></div>
21
+ <div class="oasis-field"><label class="oasis-label">Last Name</label><input class="oasis-input" type="text" [(ngModel)]="lastName" placeholder="Doe" /></div>
22
+ </div>
23
+ <div class="oasis-field"><label class="oasis-label">Email</label><input class="oasis-input" type="email" [(ngModel)]="email" placeholder="name@example.com" /></div>
24
+ <div class="oasis-field"><label class="oasis-label">Subject</label><input class="oasis-input" type="text" [(ngModel)]="subject" placeholder="How can we help?" /></div>
25
+ <div class="oasis-field"><label class="oasis-label">Message</label><textarea class="oasis-input oasis-textarea" [(ngModel)]="message" placeholder="Your message…" rows="4"></textarea></div>
26
+ <button class="oasis-btn" [disabled]="loading()" (click)="submit()">{{ loading() ? 'Sending…' : 'Send Message' }}</button>
27
+ }
28
+ </div>
29
+ `,
30
+ styles: [`
31
+ .ct-shell{display:flex;flex-direction:column;gap:18px}
32
+ .ct-header{text-align:center}
33
+ .ct-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}
34
+ .ct-sub{font-size:13px;color:#7a9bbf;margin:0}
35
+ .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}
36
+ .oasis-auth-success{background:rgba(72,220,130,.12);border:1px solid rgba(72,220,130,.3);color:#48dc82;border-radius:8px;padding:10px 14px;font-size:13px}
37
+ .oasis-grid-2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
38
+ .oasis-field{display:flex;flex-direction:column;gap:6px}
39
+ .oasis-label{font-size:12px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase}
40
+ .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;font-family:inherit}
41
+ .oasis-input:focus{border-color:rgba(0,200,255,.5)}
42
+ .oasis-textarea{resize:vertical;min-height:100px}
43
+ .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}
44
+ .oasis-btn:disabled{opacity:.5;cursor:not-allowed}
45
+ `]
46
+ })
47
+ export class ContactComponent {
48
+ firstName = ''; lastName = ''; email = ''; subject = ''; message = '';
49
+ loading = signal(false); error = signal(''); sent = signal(false);
50
+
51
+ async submit() {
52
+ if (!this.email || !this.message) { this.error.set('Please fill in your email and message.'); return; }
53
+ this.loading.set(true); this.error.set('');
54
+ try {
55
+ await fetch(`https://formsubmit.co/ajax/davidellams@hotmail.com`, {
56
+ method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
57
+ body: JSON.stringify({ firstName: this.firstName, lastName: this.lastName, email: this.email, subject: this.subject, message: this.message })
58
+ });
59
+ this.sent.set(true);
60
+ } catch (e: any) {
61
+ this.error.set(e?.message ?? 'Send failed. Please try again.');
62
+ } finally { this.loading.set(false); }
63
+ }
64
+ }
@@ -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-add-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">Add 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">Description</label><input class="mf-inp" [(ngModel)]="form['description']" placeholder="Description..." /></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 AddDataComponent {
40
+ @Input() show = false;
41
+ @Output() close = new EventEmitter<void>();
42
+ form: Record<string,string> = {
43
+ 'dataname': '',
44
+ 'datatype': '',
45
+ 'value': '',
46
+ 'provider': '',
47
+ 'description': ''
48
+ };
49
+ save() { this.close.emit(); }
50
+ }