@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
@@ -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-quest',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="vq-shell">
10
+ <div class="vq-header"><span>⚔️</span><h2 class="vq-title">View Quests</h2></div>
11
+ <div class="vq-list">
12
+ @for (q of quests(); track q.id) {
13
+ <div class="vq-card">
14
+ <div class="vq-difficulty vq-difficulty--{{ q.difficulty.toLowerCase() }}">{{ q.difficulty }}</div>
15
+ <div class="vq-name">{{ q.name }}</div>
16
+ <div class="vq-desc">{{ q.description }}</div>
17
+ <div class="vq-meta"><span>Reward: {{ q.reward }}</span><span>Level {{ q.level }}+</span></div>
18
+ </div>
19
+ }
20
+ </div>
21
+ <div class="vq-footer"><button class="vq-btn" (click)="close.emit()">Close</button></div>
22
+ </div>
23
+ `,
24
+ styles: [`
25
+ .vq-shell{display:flex;flex-direction:column;gap:16px;padding:20px}
26
+ .vq-header{display:flex;align-items:center;gap:10px}
27
+ .vq-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
28
+ .vq-list{display:flex;flex-direction:column;gap:10px}
29
+ .vq-card{background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:8px}
30
+ .vq-difficulty{font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;border-radius:999px;padding:2px 10px;align-self:flex-start;border:1px solid currentColor}
31
+ .vq-difficulty--easy{color:#00c864;border-color:rgba(0,200,100,.3)}
32
+ .vq-difficulty--medium{color:#ffb43c;border-color:rgba(255,180,60,.3)}
33
+ .vq-difficulty--hard{color:#ff5050;border-color:rgba(255,80,80,.3)}
34
+ .vq-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff}
35
+ .vq-desc{font-size:12px;color:#a8bfd8;line-height:1.5}
36
+ .vq-meta{display:flex;gap:16px;font-size:11px;color:#7a9bbf}
37
+ .vq-footer{display:flex;justify-content:flex-end}
38
+ .vq-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 ViewQuestComponent {
42
+ @Input() show = false;
43
+ @Output() close = new EventEmitter<void>();
44
+ quests = signal([
45
+ { id:1, name:'The Lost Artifact', description:'Recover the ancient artifact from the Crystal Caves.', difficulty:'Hard', reward:'500 SEEDS + 200 XP', level:10 },
46
+ { id:2, name:'Merchant Escort', description:'Safely escort the merchant caravan through the danger zone.', difficulty:'Medium', reward:'150 SEEDS + 75 XP', level:5 },
47
+ { id:3, name:'Herb Gathering', description:'Collect 10 moonblossoms from the Enchanted Forest.', difficulty:'Easy', reward:'25 SEEDS + 20 XP', level:1 },
48
+ ]);
49
+ }
@@ -0,0 +1,68 @@
1
+ import { Component, signal, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { OASISClient } from '@oasisomniverse/web4-api';
5
+
6
+ @Component({
7
+ selector: 'oasis-reset-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">Reset Password</h2>
14
+ <p class="oasis-auth-sub">Enter your new password below.</p>
15
+ </div>
16
+ @if (error()) { <div class="oasis-auth-error">{{ error() }}</div> }
17
+ @if (!done()) {
18
+ <div class="oasis-field">
19
+ <label class="oasis-label" for="rp-pwd">New Password</label>
20
+ <input class="oasis-input" id="rp-pwd" type="password" [(ngModel)]="password" placeholder="min 6 characters" autocomplete="new-password" />
21
+ </div>
22
+ <div class="oasis-field">
23
+ <label class="oasis-label" for="rp-cpwd">Confirm Password</label>
24
+ <input class="oasis-input" id="rp-cpwd" type="password" [(ngModel)]="confirmPassword" placeholder="••••••••" autocomplete="new-password" />
25
+ </div>
26
+ <button class="oasis-btn" type="button" [disabled]="loading()" (click)="submit()">
27
+ {{ loading() ? 'Resetting…' : 'Reset Password' }}
28
+ </button>
29
+ } @else {
30
+ <div class="oasis-auth-success">✅ Password reset! <a class="oasis-link" (click)="switchTo.emit('login')" href="javascript:void(0)">Beam In</a></div>
31
+ }
32
+ </div>
33
+ `,
34
+ styles: [`
35
+ .oasis-auth-form{display:flex;flex-direction:column;gap:20px}
36
+ .oasis-auth-header{text-align:center}
37
+ .oasis-auth-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}
38
+ .oasis-auth-sub{font-size:13px;color:#7a9bbf;margin:0}
39
+ .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}
40
+ .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}
41
+ .oasis-field{display:flex;flex-direction:column;gap:6px}
42
+ .oasis-label{font-size:12px;font-weight:600;letter-spacing:.06em;color:#7a9bbf;text-transform:uppercase}
43
+ .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}
44
+ .oasis-input:focus{border-color:rgba(0,200,255,.5)}
45
+ .oasis-link{color:#00c8ff;text-decoration:none;cursor:pointer}
46
+ .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}
47
+ .oasis-btn:disabled{opacity:.5;cursor:not-allowed}
48
+ `]
49
+ })
50
+ export class ResetPasswordComponent {
51
+ @Output() switchTo = new EventEmitter<'login'>();
52
+ password = ''; confirmPassword = '';
53
+ loading = signal(false); error = signal(''); done = signal(false);
54
+
55
+ async submit() {
56
+ if (!this.password || !this.confirmPassword) { this.error.set('Please fill in all fields.'); return; }
57
+ if (this.password !== this.confirmPassword) { this.error.set('Passwords do not match.'); return; }
58
+ if (this.password.length < 6) { this.error.set('Password must be at least 6 characters.'); return; }
59
+ this.loading.set(true); this.error.set('');
60
+ try {
61
+ const oasis = new OASISClient({ baseUrl: 'https://api.web4.oasisomniverse.one' });
62
+ await (oasis.auth as any).resetPassword({ password: this.password });
63
+ this.done.set(true);
64
+ } catch (e: any) {
65
+ this.error.set(e?.message ?? 'Reset failed. Please try again.');
66
+ } finally { this.loading.set(false); }
67
+ }
68
+ }
@@ -0,0 +1,80 @@
1
+ import { Component, signal } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { OASISClient } from '@oasisomniverse/web4-api';
5
+
6
+ interface AvatarResult { id: string; username: string; firstName: string; lastName: string; karma: number }
7
+
8
+ @Component({
9
+ selector: 'oasis-search-avatars',
10
+ standalone: true,
11
+ imports: [CommonModule, FormsModule],
12
+ template: `
13
+ <div class="sa-shell">
14
+ <div class="sa-header">
15
+ <h2 class="sa-title">🔍 Search Avatars</h2>
16
+ <p class="sa-sub">Find other OASIS avatars by username or name.</p>
17
+ </div>
18
+ <div class="sa-search-row">
19
+ <input class="oasis-input" type="text" [(ngModel)]="query" placeholder="Search by username…" (keydown.enter)="search()" />
20
+ <button class="oasis-btn" [disabled]="loading() || !query.trim()" (click)="search()">{{ loading() ? '…' : 'Search' }}</button>
21
+ </div>
22
+ @if (error()) { <div class="oasis-auth-error">{{ error() }}</div> }
23
+ @if (results().length > 0) {
24
+ <div class="sa-results">
25
+ @for (a of results(); track a.id) {
26
+ <div class="sa-card">
27
+ <div class="sa-avatar-icon">👤</div>
28
+ <div class="sa-info">
29
+ <div class="sa-username">{{ a.username }}</div>
30
+ <div class="sa-name">{{ a.firstName }} {{ a.lastName }}</div>
31
+ </div>
32
+ <div class="sa-karma">{{ a.karma }} karma</div>
33
+ </div>
34
+ }
35
+ </div>
36
+ } @else if (searched() && !loading()) {
37
+ <div class="sa-empty">No avatars found for "{{ query }}".</div>
38
+ }
39
+ </div>
40
+ `,
41
+ styles: [`
42
+ .sa-shell{display:flex;flex-direction:column;gap:18px}
43
+ .sa-header{text-align:center}
44
+ .sa-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}
45
+ .sa-sub{font-size:13px;color:#7a9bbf;margin:0}
46
+ .sa-search-row{display:flex;gap:10px}
47
+ .oasis-input{flex:1;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;transition:border-color .2s;font-family:inherit}
48
+ .oasis-input:focus{border-color:rgba(0,200,255,.5)}
49
+ .oasis-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 20px;cursor:pointer;white-space:nowrap;transition:opacity .2s}
50
+ .oasis-btn:disabled{opacity:.4;cursor:not-allowed}
51
+ .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}
52
+ .sa-results{display:flex;flex-direction:column;gap:10px}
53
+ .sa-card{display:flex;align-items:center;gap:14px;padding:14px 16px;background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:10px}
54
+ .sa-avatar-icon{font-size:28px}
55
+ .sa-info{flex:1}
56
+ .sa-username{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff}
57
+ .sa-name{font-size:12px;color:#7a9bbf;margin-top:2px}
58
+ .sa-karma{font-size:12px;color:#48dc82;font-weight:600;white-space:nowrap}
59
+ .sa-empty{text-align:center;color:#4a6a88;font-size:14px;padding:24px}
60
+ `]
61
+ })
62
+ export class SearchAvatarsComponent {
63
+ query = '';
64
+ loading = signal(false); error = signal(''); searched = signal(false);
65
+ results = signal<AvatarResult[]>([]);
66
+ private readonly API = 'https://api.web4.oasisomniverse.one';
67
+
68
+ async search() {
69
+ if (!this.query.trim()) return;
70
+ this.loading.set(true); this.error.set(''); this.searched.set(false); this.results.set([]);
71
+ try {
72
+ const oasis = new OASISClient({ baseUrl: this.API });
73
+ const res: any = await (oasis.avatar as any).searchAvatars({ searchQuery: this.query });
74
+ this.results.set(Array.isArray(res?.result) ? res.result : []);
75
+ this.searched.set(true);
76
+ } catch (e: any) {
77
+ this.error.set(e?.message ?? 'Search failed.');
78
+ } finally { this.loading.set(false); }
79
+ }
80
+ }
@@ -0,0 +1,44 @@
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-accept-invite',
7
+ standalone: true,
8
+ imports: [CommonModule, FormsModule],
9
+ template: `
10
+ <div class="ai-shell">
11
+ <div class="ai-header"><span>🤝</span><h2 class="ai-title">Accept Invite</h2></div>
12
+ <div class="ai-field"><label class="ai-lbl">Invite Code</label><input class="ai-inp" [(ngModel)]="code" placeholder="Enter your invite code..." /></div>
13
+ @if (message()) { <div class="ai-message">{{ message() }}</div> }
14
+ <div class="ai-footer">
15
+ <button class="ai-accept" (click)="accept()">Accept</button>
16
+ <button class="ai-cancel" (click)="close.emit()">Cancel</button>
17
+ </div>
18
+ </div>
19
+ `,
20
+ styles: [`
21
+ .ai-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
22
+ .ai-header{display:flex;align-items:center;gap:10px}
23
+ .ai-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
24
+ .ai-field{display:flex;flex-direction:column;gap:6px}
25
+ .ai-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
26
+ .ai-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}
27
+ .ai-inp:focus{border-color:rgba(0,200,255,.6)}
28
+ .ai-message{background:rgba(0,200,100,.08);border:1px solid rgba(0,200,100,.2);border-radius:8px;color:#00c864;font-size:13px;padding:12px 16px}
29
+ .ai-footer{display:flex;gap:10px;justify-content:flex-end}
30
+ .ai-accept{background:linear-gradient(135deg,#00c864,#007840);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:12px;font-weight:700;padding:10px 22px;cursor:pointer}
31
+ .ai-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}
32
+ `]
33
+ })
34
+ export class AcceptInviteComponent {
35
+ @Input() show = false;
36
+ @Output() close = new EventEmitter<void>();
37
+ code = '';
38
+ message = signal('');
39
+ accept() {
40
+ if (!this.code.trim()) return;
41
+ this.message.set('Invite accepted! Welcome to the OASIS community.');
42
+ setTimeout(() => this.close.emit(), 1500);
43
+ }
44
+ }
@@ -0,0 +1,42 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-donate-seeds',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="mf-shell">
10
+ <div class="mf-header"><span>🌱</span><h2 class="mf-title">Donate Seeds</h2></div>
11
+ <div class="mf-body">
12
+ <div class="mf-field"><label class="mf-lbl">Recipient Avatar</label><input class="mf-inp" placeholder="Recipient Avatar..." /></div>
13
+ <div class="mf-field"><label class="mf-lbl">Amount</label><input class="mf-inp" placeholder="Amount..." /></div>
14
+ <div class="mf-field"><label class="mf-lbl">Organization</label><input class="mf-inp" placeholder="Organization..." /></div>
15
+ <div class="mf-field"><label class="mf-lbl">Note</label><input class="mf-inp" placeholder="Note..." /></div>
16
+ <div class="mf-field"><label class="mf-lbl">Reference</label><input class="mf-inp" placeholder="Reference..." /></div>
17
+ </div>
18
+ <div class="mf-footer">
19
+ <button class="mf-save" (click)="save()">Save</button>
20
+ <button class="mf-cancel" (click)="close.emit()">Cancel</button>
21
+ </div>
22
+ </div>
23
+ `,
24
+ styles: [`
25
+ .mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
26
+ .mf-header{display:flex;align-items:center;gap:10px}
27
+ .mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
28
+ .mf-body{display:flex;flex-direction:column;gap:12px}
29
+ .mf-field{display:flex;flex-direction:column;gap:5px}
30
+ .mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
31
+ .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}
32
+ .mf-inp:focus{border-color:rgba(0,200,255,.6)}
33
+ .mf-footer{display:flex;gap:10px;justify-content:flex-end}
34
+ .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}
35
+ .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}
36
+ `]
37
+ })
38
+ export class DonateSeedsComponent {
39
+ @Input() show = false;
40
+ @Output() close = new EventEmitter<void>();
41
+ save() { this.close.emit(); }
42
+ }
@@ -0,0 +1,42 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-manage-seeds',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="mf-shell">
10
+ <div class="mf-header"><span>⚙️</span><h2 class="mf-title">Manage Seeds</h2></div>
11
+ <div class="mf-body">
12
+ <div class="mf-field"><label class="mf-lbl">Seeds Account No</label><input class="mf-inp" placeholder="Seeds Account No..." /></div>
13
+ <div class="mf-field"><label class="mf-lbl">Default Provider</label><input class="mf-inp" placeholder="Default Provider..." /></div>
14
+ <div class="mf-field"><label class="mf-lbl">Auto-Plant Setting</label><input class="mf-inp" placeholder="Auto-Plant Setting..." /></div>
15
+ <div class="mf-field"><label class="mf-lbl">Min Balance Alert</label><input class="mf-inp" placeholder="Min Balance Alert..." /></div>
16
+ <div class="mf-field"><label class="mf-lbl">Notifications</label><input class="mf-inp" placeholder="Notifications..." /></div>
17
+ </div>
18
+ <div class="mf-footer">
19
+ <button class="mf-save" (click)="save()">Save</button>
20
+ <button class="mf-cancel" (click)="close.emit()">Cancel</button>
21
+ </div>
22
+ </div>
23
+ `,
24
+ styles: [`
25
+ .mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
26
+ .mf-header{display:flex;align-items:center;gap:10px}
27
+ .mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
28
+ .mf-body{display:flex;flex-direction:column;gap:12px}
29
+ .mf-field{display:flex;flex-direction:column;gap:5px}
30
+ .mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
31
+ .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}
32
+ .mf-inp:focus{border-color:rgba(0,200,255,.6)}
33
+ .mf-footer{display:flex;gap:10px;justify-content:flex-end}
34
+ .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}
35
+ .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}
36
+ `]
37
+ })
38
+ export class ManageSeedsComponent {
39
+ @Input() show = false;
40
+ @Output() close = new EventEmitter<void>();
41
+ save() { this.close.emit(); }
42
+ }
@@ -0,0 +1,41 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-pay-with-seeds',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="mf-shell">
10
+ <div class="mf-header"><span>💰</span><h2 class="mf-title">Pay with Seeds</h2></div>
11
+ <div class="mf-body">
12
+ <div class="mf-field"><label class="mf-lbl">Recipient</label><input class="mf-inp" placeholder="Recipient..." /></div>
13
+ <div class="mf-field"><label class="mf-lbl">Amount</label><input class="mf-inp" placeholder="Amount..." /></div>
14
+ <div class="mf-field"><label class="mf-lbl">Memo</label><input class="mf-inp" placeholder="Memo..." /></div>
15
+ <div class="mf-field"><label class="mf-lbl">Reference</label><input class="mf-inp" placeholder="Reference..." /></div>
16
+ </div>
17
+ <div class="mf-footer">
18
+ <button class="mf-save" (click)="save()">Save</button>
19
+ <button class="mf-cancel" (click)="close.emit()">Cancel</button>
20
+ </div>
21
+ </div>
22
+ `,
23
+ styles: [`
24
+ .mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
25
+ .mf-header{display:flex;align-items:center;gap:10px}
26
+ .mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
27
+ .mf-body{display:flex;flex-direction:column;gap:12px}
28
+ .mf-field{display:flex;flex-direction:column;gap:5px}
29
+ .mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
30
+ .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}
31
+ .mf-inp:focus{border-color:rgba(0,200,255,.6)}
32
+ .mf-footer{display:flex;gap:10px;justify-content:flex-end}
33
+ .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}
34
+ .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}
35
+ `]
36
+ })
37
+ export class PayWithSeedsComponent {
38
+ @Input() show = false;
39
+ @Output() close = new EventEmitter<void>();
40
+ save() { this.close.emit(); }
41
+ }
@@ -0,0 +1,41 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-reward-seeds',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="mf-shell">
10
+ <div class="mf-header"><span>🏆</span><h2 class="mf-title">Reward Seeds</h2></div>
11
+ <div class="mf-body">
12
+ <div class="mf-field"><label class="mf-lbl">Avatar to Reward</label><input class="mf-inp" placeholder="Avatar to Reward..." /></div>
13
+ <div class="mf-field"><label class="mf-lbl">Amount</label><input class="mf-inp" placeholder="Amount..." /></div>
14
+ <div class="mf-field"><label class="mf-lbl">Reason</label><input class="mf-inp" placeholder="Reason..." /></div>
15
+ <div class="mf-field"><label class="mf-lbl">Quest/Mission Reference</label><input class="mf-inp" placeholder="Quest/Mission Reference..." /></div>
16
+ </div>
17
+ <div class="mf-footer">
18
+ <button class="mf-save" (click)="save()">Save</button>
19
+ <button class="mf-cancel" (click)="close.emit()">Cancel</button>
20
+ </div>
21
+ </div>
22
+ `,
23
+ styles: [`
24
+ .mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
25
+ .mf-header{display:flex;align-items:center;gap:10px}
26
+ .mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
27
+ .mf-body{display:flex;flex-direction:column;gap:12px}
28
+ .mf-field{display:flex;flex-direction:column;gap:5px}
29
+ .mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
30
+ .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}
31
+ .mf-inp:focus{border-color:rgba(0,200,255,.6)}
32
+ .mf-footer{display:flex;gap:10px;justify-content:flex-end}
33
+ .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}
34
+ .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}
35
+ `]
36
+ })
37
+ export class RewardSeedsComponent {
38
+ @Input() show = false;
39
+ @Output() close = new EventEmitter<void>();
40
+ save() { this.close.emit(); }
41
+ }
@@ -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-seeds',
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 Seeds</h2></div>
12
+ <div class="sf-row"><input class="sf-input" [(ngModel)]="query" placeholder="Search by avatar or transaction ID..." (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 SearchSeedsComponent {
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,75 @@
1
+ import { Component, signal } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ interface Seed { id: string; name: string; type: string; rarity: string; karma: number; planted: boolean }
5
+
6
+ @Component({
7
+ selector: 'oasis-seeds',
8
+ standalone: true,
9
+ imports: [CommonModule],
10
+ template: `
11
+ <div class="seeds-shell">
12
+ <div class="seeds-header">
13
+ <h2 class="seeds-title">🌱 Seeds</h2>
14
+ <p class="seeds-sub">Plant seeds to grow your OASIS avatar and earn karma.</p>
15
+ </div>
16
+ <div class="seeds-grid">
17
+ @for (s of seeds(); track s.id) {
18
+ <div class="seed-card" [class.seed-card--planted]="s.planted">
19
+ <div class="seed-rarity seed-rarity--{{ s.rarity.toLowerCase() }}">{{ s.rarity }}</div>
20
+ <div class="seed-icon">{{ seedIcon(s.type) }}</div>
21
+ <div class="seed-name">{{ s.name }}</div>
22
+ <div class="seed-type">{{ s.type }}</div>
23
+ <div class="seed-karma">+{{ s.karma }} Karma</div>
24
+ @if (!s.planted) {
25
+ <button class="seed-btn" (click)="plant(s)">Plant Seed</button>
26
+ } @else {
27
+ <div class="seed-planted-label">🌿 Growing…</div>
28
+ }
29
+ </div>
30
+ }
31
+ </div>
32
+ </div>
33
+ `,
34
+ styles: [`
35
+ .seeds-shell{display:flex;flex-direction:column;gap:20px}
36
+ .seeds-header{text-align:center}
37
+ .seeds-title{font-family:'Orbitron',sans-serif;font-size:20px;color:#fff;margin:0 0 6px}
38
+ .seeds-sub{font-size:13px;color:#7a9bbf;margin:0}
39
+ .seeds-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:16px}
40
+ .seed-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}
41
+ .seed-card:hover{border-color:rgba(0,200,255,.35)}
42
+ .seed-card--planted{border-color:rgba(72,220,130,.3);background:rgba(72,220,130,.04)}
43
+ .seed-rarity{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;border-radius:999px;padding:3px 10px;border:1px solid currentColor}
44
+ .seed-rarity--common{color:#7a9bbf;border-color:rgba(122,155,191,.3)}
45
+ .seed-rarity--rare{color:#5ba8ff;border-color:rgba(91,168,255,.3)}
46
+ .seed-rarity--epic{color:#b87fff;border-color:rgba(184,127,255,.3)}
47
+ .seed-rarity--legendary{color:#ffb43c;border-color:rgba(255,180,60,.3)}
48
+ .seed-icon{font-size:36px}
49
+ .seed-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff;text-align:center}
50
+ .seed-type{font-size:11px;color:#7a9bbf}
51
+ .seed-karma{font-size:12px;color:#48dc82;font-weight:600}
52
+ .seed-btn{width:100%;background:linear-gradient(135deg,#00c8ff,#0080ff);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}
53
+ .seed-btn:hover{opacity:.85}
54
+ .seed-planted-label{font-size:12px;color:#48dc82;font-weight:600}
55
+ `]
56
+ })
57
+ export class SeedsComponent {
58
+ seeds = signal<Seed[]>([
59
+ { id: '1', name: 'Wisdom Seed', type: 'Spiritual', rarity: 'Rare', karma: 50, planted: false },
60
+ { id: '2', name: 'Healing Seed', type: 'Health', rarity: 'Common', karma: 20, planted: false },
61
+ { id: '3', name: 'Unity Seed', type: 'Community', rarity: 'Epic', karma: 150, planted: false },
62
+ { id: '4', name: 'Truth Seed', type: 'Knowledge', rarity: 'Legendary', karma: 500, planted: false },
63
+ { id: '5', name: 'Love Seed', type: 'Spiritual', rarity: 'Common', karma: 30, planted: false },
64
+ { id: '6', name: 'Peace Seed', type: 'Community', rarity: 'Rare', karma: 75, planted: false },
65
+ ]);
66
+
67
+ seedIcon(type: string): string {
68
+ const m: Record<string, string> = { Spiritual: '✨', Health: '💚', Community: '🤝', Knowledge: '📖' };
69
+ return m[type] ?? '🌱';
70
+ }
71
+
72
+ plant(s: Seed) {
73
+ this.seeds.update(list => list.map(x => x.id === s.id ? { ...x, planted: true } : x));
74
+ }
75
+ }
@@ -0,0 +1,41 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-send-invite',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="mf-shell">
10
+ <div class="mf-header"><span>✉️</span><h2 class="mf-title">Send Invite</h2></div>
11
+ <div class="mf-body">
12
+ <div class="mf-field"><label class="mf-lbl">Recipient Name</label><input class="mf-inp" placeholder="Recipient Name..." /></div>
13
+ <div class="mf-field"><label class="mf-lbl">Email Address</label><input class="mf-inp" placeholder="Email Address..." /></div>
14
+ <div class="mf-field"><label class="mf-lbl">Personalised Message</label><input class="mf-inp" placeholder="Personalised Message..." /></div>
15
+ <div class="mf-field"><label class="mf-lbl">Seeds Bonus Amount</label><input class="mf-inp" placeholder="Seeds Bonus Amount..." /></div>
16
+ </div>
17
+ <div class="mf-footer">
18
+ <button class="mf-save" (click)="save()">Save</button>
19
+ <button class="mf-cancel" (click)="close.emit()">Cancel</button>
20
+ </div>
21
+ </div>
22
+ `,
23
+ styles: [`
24
+ .mf-shell{display:flex;flex-direction:column;gap:18px;padding:20px}
25
+ .mf-header{display:flex;align-items:center;gap:10px}
26
+ .mf-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
27
+ .mf-body{display:flex;flex-direction:column;gap:12px}
28
+ .mf-field{display:flex;flex-direction:column;gap:5px}
29
+ .mf-lbl{font-size:11px;font-weight:700;letter-spacing:.07em;color:#7a9bbf;text-transform:uppercase}
30
+ .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}
31
+ .mf-inp:focus{border-color:rgba(0,200,255,.6)}
32
+ .mf-footer{display:flex;gap:10px;justify-content:flex-end}
33
+ .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}
34
+ .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}
35
+ `]
36
+ })
37
+ export class SendInviteComponent {
38
+ @Input() show = false;
39
+ @Output() close = new EventEmitter<void>();
40
+ save() { this.close.emit(); }
41
+ }
@@ -0,0 +1,55 @@
1
+ import { Component, Input, Output, EventEmitter, signal } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+
4
+ @Component({
5
+ selector: 'oasis-view-organizations',
6
+ standalone: true,
7
+ imports: [CommonModule],
8
+ template: `
9
+ <div class="vo-shell">
10
+ <div class="vo-header"><span>🏢</span><h2 class="vo-title">View Organizations</h2></div>
11
+ <div class="vo-list">
12
+ @for (org of organizations(); track org.id) {
13
+ <div class="vo-card">
14
+ <div class="vo-icon">{{ org.icon }}</div>
15
+ <div class="vo-info">
16
+ <div class="vo-name">{{ org.name }}</div>
17
+ <div class="vo-members">{{ org.members }} members</div>
18
+ <div class="vo-desc">{{ org.description }}</div>
19
+ </div>
20
+ <button class="vo-join" (click)="join(org.id)">{{ org.joined ? 'Joined' : 'Join' }}</button>
21
+ </div>
22
+ }
23
+ </div>
24
+ <div class="vo-footer"><button class="vo-btn" (click)="close.emit()">Close</button></div>
25
+ </div>
26
+ `,
27
+ styles: [`
28
+ .vo-shell{display:flex;flex-direction:column;gap:16px;padding:20px}
29
+ .vo-header{display:flex;align-items:center;gap:10px}
30
+ .vo-title{font-family:'Orbitron',sans-serif;font-size:18px;color:#fff;margin:0}
31
+ .vo-list{display:flex;flex-direction:column;gap:10px}
32
+ .vo-card{display:flex;align-items:center;gap:14px;background:rgba(255,255,255,.04);border:1px solid rgba(0,200,255,.12);border-radius:10px;padding:14px 16px}
33
+ .vo-icon{font-size:32px;flex-shrink:0}
34
+ .vo-info{flex:1}
35
+ .vo-name{font-family:'Orbitron',sans-serif;font-size:13px;color:#fff}
36
+ .vo-members{font-size:11px;color:#7a9bbf;margin-top:2px}
37
+ .vo-desc{font-size:12px;color:#a8bfd8;margin-top:4px}
38
+ .vo-join{background:linear-gradient(135deg,#00c8ff,#0080ff);border:none;border-radius:8px;color:#fff;font-family:'Orbitron',sans-serif;font-size:11px;font-weight:700;padding:8px 14px;cursor:pointer;flex-shrink:0;white-space:nowrap}
39
+ .vo-footer{display:flex;justify-content:flex-end}
40
+ .vo-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}
41
+ `]
42
+ })
43
+ export class ViewOrganizationsComponent {
44
+ @Input() show = false;
45
+ @Output() close = new EventEmitter<void>();
46
+ organizations = signal([
47
+ { id:1, icon:'🌿', name:'SEEDS Collective', members:2840, description:'Regenerative economics community.', joined:true },
48
+ { id:2, icon:'🌐', name:'OASIS Builders', members:1520, description:'Open source development collective.', joined:false },
49
+ { id:3, icon:'💡', name:'Innovation DAO', members:890, description:'Funding breakthrough ideas in the OASIS.', joined:false },
50
+ { id:4, icon:'🎨', name:'Creative Guild', members:340, description:'NFT artists and digital creators.', joined:true },
51
+ ]);
52
+ join(id: number) {
53
+ this.organizations.update(list => list.map(o => o.id === id ? { ...o, joined: !o.joined } : o));
54
+ }
55
+ }