Spaces:
Running
You are a master programmer in HTML, CSS and JS. Create an app that consist in showing interactive special markers on a map, with the following requirements:
Browse filesAPP DESCRIPTION:
Core Tech Stack:
- Map: Leaflet.js with OpenStreetMap tiles.
- Markers: FontAwesome icons.
- Main Database: Firebase Firestore.
- Local Storage: For user data, interaction tracking and persistence.
- UI: Modals, buttons, search bar, tokens, notifications.
Primal goal:
- Display markers on map as FontAwesome icons with stacking order influenced by z-depth (zDepth).
- Transform user clicks in metrics.
- Create battle and archetype systems.
- User can spend tokens in shop.
Data Structures:
Firestore Collections:
- users: { userID, liquidityTokens, valueTokens, engagementTokens }
- markers: { title, body, subcategory, email, favorites, link, lat, lng, likes, shares, userID, zDepth }
Local Storage:
- Users are anonymous and have random UUID(userID).
- Stores user metrics (userData) for markers, dashboard, tokens spend, battle metrics, shop items, and archetype points, progress and branches variables (ar.(category).Var.(branch number)).
- A dictionary where keys are (markerID) for tracking one time interaction (e.g. action.(markerID)-Like) and one to track toggle state (e.g. state.(markerID)-Like) on fav/like action buttons.
UI Components & Interactions:
Map:
- Initialize Leaflet map.
- Set a default view (e.g., max zoom out).
- Fetch all markers from Firestore and display them as a full colored round FA icons 48x48px on white background.
- Clicks on markers open the marker details modal.
Top UI:
- Search Bar: Filter markers in real-time based on input, align on left.
- Token Display: Show token counts, in full name, each with different color, align on right.
Bottom UI:
- Four buttons: Dashboard, Battle Arena, Shop, Add Marker, aligned on middle, no text.
UI Interactions:
Add Marker button flow:
- Click button.
- Mouse change to crosshair.
- User click on the map.
- A modal appears with a form for title (50 chars max), body (500 chars max), dropdown for subcategory, email, link and a button "Create marker"
Create Marker button flow:
- Data from fields is saved to Firestore.
- Reward ar.(category).Var.1 random tokens.
- Set marker zDepth to (100+ar.(category).Var.3)
Marker Details Modal:
- Displays "title" and "body"
- Display Firestore Fav and Like numbers.
- Display zDepth value.
- Shows 5 button action as icons (Link, Mail, Favorite, Like, Share) aligned on middle bottom.
- If database entry for link or mail doesn't exist, icons will not be show.
- No visible spacers between fields.
Button Actions Interactions:
- Check action.(markerID)dictionary, if doesn't exist or "false" reward (1+ar.(category).Var.4) random token, add 1 to Interaction metric, set to "true" and perform respective action below. If "true", just perform respective button action below.
Button Actions:
- Link: window.open(link, '_blank').
- Mail: window.location.href = 'mailto:...'.
- Share: navigator.clipboard.writeText(https://app.holdspot.online?markerId).
- Like/Favorite: Check state.(markerID) dictionary, update data in Firestore (false++/--true) and set icon color accordingly (normal icons vs full color icon).
Notification System:
- Aligned on right middle screen
- A function showNotification(message, type) that creates a div, adds it to a container, and removes it after a timeout.
- No buttons on div, just pure info
- Needs to be called for everything: success, errors, info, battle results, wrong configuration, etc.
Dashboard Modal:
- 3 tabs: Metrics, Exchange House, Archetypes, aligned on middle.
Metrics Tab:
- Display this metrics: Markers Created, Likes Given, Favorites Made, Interactions, Share Clicked, Links Clicked, Emails Clicked, Tokens Spent, Battles Won and Battles Lost.
Exchange Tab:
- Calculate exchange rate based on total marker count. (e.g., rate = Math.max(1:100/totalMarkers)).
- UI for selecting "from" token, "to" token, amount and Exchange button on a same row.
Archetypes Tab:
- Five sub-tabs, each one with 3 rows of 1,2,3 abilities.
- Each abilities have +/- buttons that can add or remove up to 25 points, if available.
- Link number of points spend to ar.(category).Var.(branch number)
- No titles or description, just tooltip to info about current abilities.
Shop Modal:
- 3 tabs, Boosters, Potions, Unlockers.
- Populate shop with items.
Battle Arena Modal:
- Display all active items/branches with full description.
- Start Battle button available once every 30 seconds (grey out in between).
Start Battle button flow:
- Select a random marker from all markers on the map, including user owned markers, so absolute random.
- Simulate a battle. Win/loss chances is (50+ar.(category).Var.6+(shop potion)) percent.
- Update the marker's zDepth in Firestore (+/- 1+ar.(category).Var.2) and used items, inventory, etc. in localStorage.
- Update user metrics and transform marker location to a continent/country/city.
- Display a message"You just win/lost against "continent/country/city"!", the marker icon and title (use word-wrap: break-word).
- Refresh modal info.
MAIN REQUIREMENTS:
Typography, CSS and aesthetics request:
- Design a production-ready minimalist CSS dashboard system prioritizing maximum data density layouts.
- Use IBM Plex and Inter font (xs: 0.65rem, sm: 0.7rem, base: 0.75rem, lg: 0.8rem, xl: 0.9rem).
- Use height: auto and word-wrap: break-word for title and body fields on marker and marker title on battle modal window.
- Implement soft line break on title and body, so that when inserted in add marker modal, it should reflect/appear in marker details modal.
- All modal windows have no title bar, no close button and they close BY CLICKING OUTSIDE OF THE WINDOWS.
- Make it work on all devices.
Map request:
- Remove this Leaflet.js UI elements for cleaner look (zoom and double click buttons, attribution, cursor hand, marker pop-up).
- Restrict zoom out to 3 and zoom in to 12.
- Restict pan to infinite left/right and up/down.
- User can pan/move map with mouse.
MAIN REQUESTS:
- Be sure every aspect of the game logic is implemented and function perfectly.
- Use relevant FA icons as much as possible.
ARCHETYPE ABILITIES/TREE:
- Basic Proficiency - Creating "archetype category name" markers award "ar.(category).Var.1" random token.
- Battle Tactics - Battle against "archetype category name" markers add "ar.(category).Var.2" (lose++/--win) to ZDepth value.
- Creation Mastery - Creating "archetype category name" markers add "ar.(category).Var.3" to the default ZDepth value.
- Interaction Boost - First time interaction with "archetype category name" markers give aditional "ar.(category).Var.4" random token.
- Economic Advantage - Shop items that affect "archetype category name" archetype are "ar.(category).Var.5"% cheaper.
- Combat Superiority - Improve Battle Arena general winning against "archetype category name" markers chances by "ar.(category).Var.6"%.
Same template for Merchant, Explorer, Diplomat, Artisan and Sentinel tree.
FIRESTORE DATA:
const firebaseConfig = {
apiKey: "AIzaSyB5NffQxssjZKEAUyp8Wyt2Y3rJmJOVfx4",
authDomain: "holdspot-977d3.firebaseapp.com",
projectId: "holdspot-977d3",
storageBucket: "holdspot-977d3.firebasestorage.app",
messagingSenderId: "969993737434",
appId: "1:969993737434:web:17f43caab0bf46966701e3"
};
SHOP ITEMS:
Boosters
- Give additional "ar.(category).Var.4" tokens per Like/Fav for 1 minute / available once per 30 minute / cost 10*(ar.(category).Var.5/100) Value token
- Give additional "ar.(category).Var.4" tokens per Like/Fav for 1 minute / available once per 30 minute / cost 10*(ar.(category).Var.5/100) Liquidity token
- Give additional "ar.(category).Var.4" tokens per Like/Fav for 1 minute / available once per 30 minute / cost 10*(ar.(category).Var.5/100) Engagement token
Potions:
- Next battle have 10% general win chance / available once per 5 minute / cost 10*(ar.(category).Var.5/100) Value token
- Next battle have 10% general win chance / available once per 5 minute / cost 10*(ar.(category).Var.5/100) Liquidity token
- Next battle have 10% general win chance / available once per 5 minute / cost 10*(ar.(category).Var.5/100) Engagement token
Unlockers:
- Gain 1 archetype point / available once per 360 minute / cost 10*(ar.(category).Var.5/100) Value token
- Gain 1 archetype point / available once per 360 minute / cost 10*(ar.(category).Var.5/100) Liquidity token
- Gain 1 archetype point / available once per 360 minute / cost 10*(ar.(category).Var.5/100) Engagement token
ICONS/CATEGORY/SUBCATEGORY:
const iconColors = {
'fa-hand-holding-usd': '#4CAF50',
'fa-shopping-cart': '#2196F3',
'fa-exchange-alt': '#FF9800',
'fa-gift': '#9C27B0',
'fa-file-contract': '#607D8B',
'fa-plane': '#2196F3',
'fa-bed': '#9C27B0',
'fa-map-marker-alt': '#4CAF50',
'fa-users': '#FF9800',
'fa-route': '#607D8B',
'fa-briefcase': '#607D8B',
'fa-user-tie': '#2196F3',
'fa-concierge-bell': '#4CAF50',
'fa-heart': '#E91E63',
'fa-lightbulb': '#FFC107',
'fa-palette': '#9C27B0',
'fa-film': '#E91E63',
'fa-hiking': '#4CAF50',
'fa-glass-cheers': '#FF9800',
'fa-star': '#2196F3',
'fa-exclamation-triangle': '#F44336',
'fa-eye': '#2196F3',
'fa-clipboard-list': '#4CAF50',
'fa-compass': '#FF9800',
'fa-bolt': '#9C27B0'
};
const subcategoryNames = {
'fa-hand-holding-usd': 'Sell',
'fa-shopping-cart': 'Buy',
'fa-exchange-alt': 'Exchange',
'fa-gift': 'Gifts',
'fa-file-contract': 'Lease',
'fa-plane': 'Travel',
'fa-bed': 'Accommodation',
'fa-map-marker-alt': 'Places',
'fa-users': 'Communities',
'fa-route': 'Trips',
'fa-briefcase': 'Jobs',
'fa-user-tie': 'Professional',
'fa-concierge-bell': 'Services',
'fa-heart': 'Dating',
- README.md +8 -5
- index.html +127 -19
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: HoldSpot 🌟
|
| 3 |
+
colorFrom: yellow
|
| 4 |
+
colorTo: pink
|
| 5 |
+
emoji: 🐳
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
|
@@ -1,19 +1,127 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>HoldSpot</title>
|
| 7 |
+
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
| 8 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
| 9 |
+
<link rel="stylesheet" href="style.css">
|
| 10 |
+
</head>
|
| 11 |
+
<body>
|
| 12 |
+
<!-- Top UI -->
|
| 13 |
+
<div class="top-ui">
|
| 14 |
+
<input type="text" id="search-bar" placeholder="Search markers...">
|
| 15 |
+
<div class="token-display">
|
| 16 |
+
<span class="liquidity-token"><i class="fas fa-tint"></i> <span id="liquidity-count">0</span></span>
|
| 17 |
+
<span class="value-token"><i class="fas fa-coins"></i> <span id="value-count">0</span></span>
|
| 18 |
+
<span class="engagement-token"><i class="fas fa-fire"></i> <span id="engagement-count">0</span></span>
|
| 19 |
+
</div>
|
| 20 |
+
</div>
|
| 21 |
+
|
| 22 |
+
<!-- Map Container -->
|
| 23 |
+
<div id="map"></div>
|
| 24 |
+
|
| 25 |
+
<!-- Bottom UI -->
|
| 26 |
+
<div class="bottom-ui">
|
| 27 |
+
<button id="dashboard-btn" class="nav-btn"><i class="fas fa-chart-line"></i></button>
|
| 28 |
+
<button id="battle-btn" class="nav-btn"><i class="fas fa-fist-raised"></i></button>
|
| 29 |
+
<button id="shop-btn" class="nav-btn"><i class="fas fa-store"></i></button>
|
| 30 |
+
<button id="add-marker-btn" class="nav-btn"><i class="fas fa-plus-circle"></i></button>
|
| 31 |
+
</div>
|
| 32 |
+
|
| 33 |
+
<!-- Modals -->
|
| 34 |
+
<div id="marker-modal" class="modal">
|
| 35 |
+
<div class="modal-content">
|
| 36 |
+
<h3 id="marker-title"></h3>
|
| 37 |
+
<p id="marker-body"></p>
|
| 38 |
+
<div class="marker-stats">
|
| 39 |
+
<span>Favorites: <span id="marker-favorites">0</span></span>
|
| 40 |
+
<span>Likes: <span id="marker-likes">0</span></span>
|
| 41 |
+
<span>Z-Depth: <span id="marker-zdepth">0</span></span>
|
| 42 |
+
</div>
|
| 43 |
+
<div class="action-buttons">
|
| 44 |
+
<button id="link-btn" class="action-btn"><i class="fas fa-link"></i></button>
|
| 45 |
+
<button id="mail-btn" class="action-btn"><i class="fas fa-envelope"></i></button>
|
| 46 |
+
<button id="fav-btn" class="action-btn"><i class="far fa-heart"></i></button>
|
| 47 |
+
<button id="like-btn" class="action-btn"><i class="far fa-thumbs-up"></i></button>
|
| 48 |
+
<button id="share-btn" class="action-btn"><i class="fas fa-share-alt"></i></button>
|
| 49 |
+
</div>
|
| 50 |
+
</div>
|
| 51 |
+
</div>
|
| 52 |
+
|
| 53 |
+
<div id="add-marker-modal" class="modal">
|
| 54 |
+
<div class="modal-content">
|
| 55 |
+
<input type="text" id="marker-title-input" placeholder="Title (max 50 chars)" maxlength="50">
|
| 56 |
+
<textarea id="marker-body-input" placeholder="Description (max 500 chars)" maxlength="500"></textarea>
|
| 57 |
+
<select id="marker-subcategory">
|
| 58 |
+
<!-- Options will be populated by JS -->
|
| 59 |
+
</select>
|
| 60 |
+
<input type="email" id="marker-email" placeholder="Email (optional)">
|
| 61 |
+
<input type="url" id="marker-link" placeholder="Link (optional)">
|
| 62 |
+
<button id="create-marker-btn">Create Marker</button>
|
| 63 |
+
</div>
|
| 64 |
+
</div>
|
| 65 |
+
|
| 66 |
+
<div id="dashboard-modal" class="modal">
|
| 67 |
+
<div class="modal-content">
|
| 68 |
+
<div class="tabs">
|
| 69 |
+
<button class="tab-btn active" data-tab="metrics">Metrics</button>
|
| 70 |
+
<button class="tab-btn" data-tab="exchange">Exchange House</button>
|
| 71 |
+
<button class="tab-btn" data-tab="archetypes">Archetypes</button>
|
| 72 |
+
</div>
|
| 73 |
+
<div class="tab-content">
|
| 74 |
+
<div id="metrics-tab" class="tab-pane active">
|
| 75 |
+
<!-- Metrics content will be populated by JS -->
|
| 76 |
+
</div>
|
| 77 |
+
<div id="exchange-tab" class="tab-pane">
|
| 78 |
+
<!-- Exchange content will be populated by JS -->
|
| 79 |
+
</div>
|
| 80 |
+
<div id="archetypes-tab" class="tab-pane">
|
| 81 |
+
<!-- Archetypes content will be populated by JS -->
|
| 82 |
+
</div>
|
| 83 |
+
</div>
|
| 84 |
+
</div>
|
| 85 |
+
</div>
|
| 86 |
+
|
| 87 |
+
<div id="shop-modal" class="modal">
|
| 88 |
+
<div class="modal-content">
|
| 89 |
+
<div class="tabs">
|
| 90 |
+
<button class="tab-btn active" data-tab="boosters">Boosters</button>
|
| 91 |
+
<button class="tab-btn" data-tab="potions">Potions</button>
|
| 92 |
+
<button class="tab-btn" data-tab="unlockers">Unlockers</button>
|
| 93 |
+
</div>
|
| 94 |
+
<div class="tab-content">
|
| 95 |
+
<div id="boosters-tab" class="tab-pane active">
|
| 96 |
+
<!-- Boosters content will be populated by JS -->
|
| 97 |
+
</div>
|
| 98 |
+
<div id="potions-tab" class="tab-pane">
|
| 99 |
+
<!-- Potions content will be populated by JS -->
|
| 100 |
+
</div>
|
| 101 |
+
<div id="unlockers-tab" class="tab-pane">
|
| 102 |
+
<!-- Unlockers content will be populated by JS -->
|
| 103 |
+
</div>
|
| 104 |
+
</div>
|
| 105 |
+
</div>
|
| 106 |
+
</div>
|
| 107 |
+
|
| 108 |
+
<div id="battle-modal" class="modal">
|
| 109 |
+
<div class="modal-content">
|
| 110 |
+
<div id="battle-info">
|
| 111 |
+
<!-- Battle info will be populated by JS -->
|
| 112 |
+
</div>
|
| 113 |
+
<button id="start-battle-btn">Start Battle</button>
|
| 114 |
+
</div>
|
| 115 |
+
</div>
|
| 116 |
+
|
| 117 |
+
<!-- Notification Container -->
|
| 118 |
+
<div id="notification-container"></div>
|
| 119 |
+
|
| 120 |
+
<!-- Scripts -->
|
| 121 |
+
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
| 122 |
+
<script src="https://www.gstatic.com/firebasejs/9.6.10/firebase-app-compat.js"></script>
|
| 123 |
+
<script src="https://www.gstatic.com/firebasejs/9.6.10/firebase-firestore-compat.js"></script>
|
| 124 |
+
<script src="script.js"></script>
|
| 125 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 126 |
+
</body>
|
| 127 |
+
</html>
|