alejandro-ao HF Staff commited on
Commit
744bca2
·
verified ·
1 Parent(s): 759147c

**Prompt: Fake Analytics Dashboard App (Full-Stack)**

Browse files

Build a complete full-stack web application that displays a **fake analytics dashboard** with **moving, auto-updating stats**. The data does **not need to be real** — it should be **randomized or oscillate within credible ranges** so the dashboard feels alive.

### Requirements

#### Frontend
- Modern UI (React, Next.js, or your default framework).
- Cards showing live metrics like:
- Active Users
- Conversion Rate
- API Latency
- Revenue Today
- Small charts that auto-update (line chart, bar chart, sparkline).
- Smooth transitions (animate value changes).

#### Backend
- Simple API endpoints that return pseudo-realistic data.
- Values should update every few seconds by:
- incrementing/decrementing within a valid range, or
- generating new random but believable values.
- Optional: WebSocket endpoint for real-time updates.

#### Behavior
- Metrics refresh every 2–5 seconds.
- Random noise added so values fluctuate credibly (e.g., ±5%).
- Charts update continuously with new points.
- No real database required — in-memory or mock functions are fine.

### Bonus
- Dark/light mode.
- Responsive layout.
- Header with a fake company name: **“NovaPulse Analytics”**.

**Goal:** produce a polished, working live dashboard that “looks real” even though all data is synthetic.

Files changed (6) hide show
  1. README.md +8 -5
  2. components/metrics-card.js +63 -0
  3. components/navbar.js +42 -0
  4. index.html +64 -19
  5. script.js +193 -0
  6. style.css +23 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Pulsefaker Analytics Dash
3
- emoji: 👀
4
- colorFrom: indigo
5
- colorTo: green
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: PulseFaker Analytics Dash
3
+ colorFrom: green
4
+ colorTo: blue
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).
components/metrics-card.js ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class MetricsCard extends HTMLElement {
2
+ static get observedAttributes() {
3
+ return ['title', 'icon', 'value', 'change', 'color'];
4
+ }
5
+
6
+ constructor() {
7
+ super();
8
+ this.attachShadow({ mode: 'open' });
9
+ }
10
+
11
+ connectedCallback() {
12
+ this.render();
13
+ }
14
+
15
+ attributeChangedCallback(name, oldValue, newValue) {
16
+ if (oldValue !== newValue) {
17
+ this.render();
18
+ }
19
+ }
20
+
21
+ render() {
22
+ const title = this.getAttribute('title') || '';
23
+ const icon = this.getAttribute('icon') || 'activity';
24
+ const value = this.getAttribute('value') || '0';
25
+ const change = parseFloat(this.getAttribute('change') || '0');
26
+ const color = this.getAttribute('color') || 'primary';
27
+
28
+ const changeClass = change > 0 ? 'change-positive' : change < 0 ? 'change-negative' : 'change-neutral';
29
+ const changeIcon = change > 0 ? 'trending-up' : change < 0 ? 'trending-down' : 'minus';
30
+ const changeText = change > 0 ? `+${change}` : change;
31
+
32
+ this.shadowRoot.innerHTML = `
33
+ <style>
34
+ .card {
35
+ transition: all 0.3s ease;
36
+ }
37
+ .card:hover {
38
+ transform: translateY(-2px);
39
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
40
+ }
41
+ </style>
42
+ <div class="card bg-white dark:bg-gray-800 rounded-xl shadow p-6 hover:shadow-md transition-all duration-300">
43
+ <div class="flex justify-between items-start">
44
+ <div>
45
+ <p class="text-gray-500 dark:text-gray-400 text-sm font-medium">${title}</p>
46
+ <h3 class="text-2xl font-bold mt-1">${value}</h3>
47
+ </div>
48
+ <div class="w-10 h-10 rounded-lg flex items-center justify-center bg-${color}-100 dark:bg-${color}-900/30">
49
+ <i data-feather="${icon}" class="text-${color}-600 dark:text-${color}-400"></i>
50
+ </div>
51
+ </div>
52
+ <div class="mt-4 flex items-center ${changeClass}">
53
+ <i data-feather="${changeIcon}" class="w-4 h-4 mr-1"></i>
54
+ <span class="text-sm font-medium">${changeText}%</span>
55
+ <span class="text-xs ml-1 opacity-70">vs last update</span>
56
+ </div>
57
+ </div>
58
+ `;
59
+ feather.replace();
60
+ }
61
+ }
62
+
63
+ customElements.define('metrics-card', MetricsCard);
components/navbar.js ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .theme-toggle:hover {
7
+ transform: rotate(30deg);
8
+ }
9
+ .theme-toggle:active {
10
+ transform: rotate(60deg);
11
+ }
12
+ </style>
13
+ <nav class="bg-white dark:bg-gray-800 shadow-sm">
14
+ <div class="container mx-auto px-4 py-4 flex justify-between items-center">
15
+ <div class="flex items-center space-x-2">
16
+ <div class="w-8 h-8 rounded-full bg-gradient-to-tr from-primary to-secondary flex items-center justify-center">
17
+ <i data-feather="activity" class="text-white w-4 h-4"></i>
18
+ </div>
19
+ <h1 class="text-xl font-bold">PulseFaker Analytics</h1>
20
+ </div>
21
+ <div class="flex items-center space-x-4">
22
+ <button class="theme-toggle p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-transform duration-300">
23
+ <i data-feather="moon" class="hidden dark:block"></i>
24
+ <i data-feather="sun" class="dark:hidden"></i>
25
+ </button>
26
+ <div class="w-8 h-8 rounded-full bg-gray-200 dark:bg-gray-600 flex items-center justify-center">
27
+ <i data-feather="user" class="text-gray-600 dark:text-gray-300"></i>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </nav>
32
+ `;
33
+
34
+ this.shadowRoot.querySelector('.theme-toggle').addEventListener('click', () => {
35
+ document.documentElement.classList.toggle('dark');
36
+ localStorage.setItem('darkMode', document.documentElement.classList.contains('dark'));
37
+ feather.replace();
38
+ });
39
+ }
40
+ }
41
+
42
+ customElements.define('custom-navbar', CustomNavbar);
index.html CHANGED
@@ -1,19 +1,64 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="h-full">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PulseFaker Analytics</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
12
+ <script>
13
+ tailwind.config = {
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ primary: '#6366f1',
18
+ secondary: '#8b5cf6',
19
+ }
20
+ }
21
+ }
22
+ }
23
+ </script>
24
+ </head>
25
+ <body class="bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100 h-full transition-colors duration-300">
26
+ <custom-navbar></custom-navbar>
27
+
28
+ <main class="container mx-auto px-4 py-8">
29
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
30
+ <metrics-card title="Active Users" icon="users" value="0" change="0" color="primary"></metrics-card>
31
+ <metrics-card title="Conversion Rate" icon="trending-up" value="0" change="0" color="secondary"></metrics-card>
32
+ <metrics-card title="API Latency" icon="clock" value="0" change="0" color="primary"></metrics-card>
33
+ <metrics-card title="Revenue Today" icon="dollar-sign" value="0" change="0" color="secondary"></metrics-card>
34
+ </div>
35
+
36
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
37
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow p-6">
38
+ <h3 class="text-lg font-semibold mb-4">User Growth</h3>
39
+ <canvas id="userChart" height="300"></canvas>
40
+ </div>
41
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow p-6">
42
+ <h3 class="text-lg font-semibold mb-4">Revenue Sources</h3>
43
+ <canvas id="revenueChart" height="300"></canvas>
44
+ </div>
45
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow p-6">
46
+ <h3 class="text-lg font-semibold mb-4">Performance Metrics</h3>
47
+ <canvas id="performanceChart" height="300"></canvas>
48
+ </div>
49
+ <div class="bg-white dark:bg-gray-800 rounded-xl shadow p-6">
50
+ <h3 class="text-lg font-semibold mb-4">Activity Timeline</h3>
51
+ <canvas id="activityChart" height="300"></canvas>
52
+ </div>
53
+ </div>
54
+ </main>
55
+
56
+ <script src="components/navbar.js"></script>
57
+ <script src="components/metrics-card.js"></script>
58
+ <script src="script.js"></script>
59
+ <script>
60
+ feather.replace();
61
+ </script>
62
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
63
+ </body>
64
+ </html>
script.js ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Initialize charts
2
+ let userChart, revenueChart, performanceChart, activityChart;
3
+
4
+ document.addEventListener('DOMContentLoaded', () => {
5
+ setupCharts();
6
+ startDataUpdates();
7
+ });
8
+
9
+ function setupCharts() {
10
+ const chartOptions = {
11
+ responsive: true,
12
+ maintainAspectRatio: false,
13
+ plugins: {
14
+ legend: {
15
+ position: 'top',
16
+ labels: {
17
+ color: window.matchMedia('(prefers-color-scheme: dark)').matches ? '#fff' : '#000'
18
+ }
19
+ }
20
+ },
21
+ scales: {
22
+ y: {
23
+ beginAtZero: true,
24
+ grid: {
25
+ color: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'
26
+ },
27
+ ticks: {
28
+ color: window.matchMedia('(prefers-color-scheme: dark)').matches ? '#fff' : '#000'
29
+ }
30
+ },
31
+ x: {
32
+ grid: {
33
+ color: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'
34
+ },
35
+ ticks: {
36
+ color: window.matchMedia('(prefers-color-scheme: dark)').matches ? '#fff' : '#000'
37
+ }
38
+ }
39
+ }
40
+ };
41
+
42
+ // User Growth Chart
43
+ const userCtx = document.getElementById('userChart').getContext('2d');
44
+ userChart = new Chart(userCtx, {
45
+ type: 'line',
46
+ data: {
47
+ labels: Array.from({length: 12}, (_, i) => `${i+1}AM`),
48
+ datasets: [{
49
+ label: 'Active Users',
50
+ data: Array(12).fill(0).map(() => Math.floor(Math.random() * 5000)),
51
+ borderColor: '#6366f1',
52
+ backgroundColor: 'rgba(99, 102, 241, 0.1)',
53
+ tension: 0.4,
54
+ fill: true
55
+ }]
56
+ },
57
+ options: chartOptions
58
+ });
59
+
60
+ // Revenue Chart
61
+ const revenueCtx = document.getElementById('revenueChart').getContext('2d');
62
+ revenueChart = new Chart(revenueCtx, {
63
+ type: 'doughnut',
64
+ data: {
65
+ labels: ['Subscriptions', 'Ads', 'Merchandise', 'Services'],
66
+ datasets: [{
67
+ data: [35, 25, 20, 20].map(v => v + Math.floor(Math.random() * 10 - 5)),
68
+ backgroundColor: [
69
+ '#6366f1',
70
+ '#8b5cf6',
71
+ '#ec4899',
72
+ '#f43f5e'
73
+ ],
74
+ borderWidth: 0
75
+ }]
76
+ },
77
+ options: {
78
+ ...chartOptions,
79
+ cutout: '70%'
80
+ }
81
+ });
82
+
83
+ // Performance Chart
84
+ const performanceCtx = document.getElementById('performanceChart').getContext('2d');
85
+ performanceChart = new Chart(performanceCtx, {
86
+ type: 'bar',
87
+ data: {
88
+ labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
89
+ datasets: [
90
+ {
91
+ label: 'Page Load (ms)',
92
+ data: Array(7).fill(0).map(() => 100 + Math.floor(Math.random() * 300)),
93
+ backgroundColor: '#6366f1'
94
+ },
95
+ {
96
+ label: 'API Response (ms)',
97
+ data: Array(7).fill(0).map(() => 50 + Math.floor(Math.random() * 150)),
98
+ backgroundColor: '#8b5cf6'
99
+ }
100
+ ]
101
+ },
102
+ options: chartOptions
103
+ });
104
+
105
+ // Activity Chart
106
+ const activityCtx = document.getElementById('activityChart').getContext('2d');
107
+ activityChart = new Chart(activityCtx, {
108
+ type: 'line',
109
+ data: {
110
+ labels: Array.from({length: 24}, (_, i) => i),
111
+ datasets: [
112
+ {
113
+ label: 'API Calls',
114
+ data: Array(24).fill(0).map((_, i) => {
115
+ const base = 500;
116
+ const hourFactor = Math.sin((i / 24) * Math.PI * 2) * 200;
117
+ const noise = Math.random() * 100 - 50;
118
+ return Math.max(0, base + hourFactor + noise);
119
+ }),
120
+ borderColor: '#6366f1',
121
+ backgroundColor: 'rgba(99, 102, 241, 0.1)',
122
+ tension: 0.3,
123
+ fill: true
124
+ }
125
+ ]
126
+ },
127
+ options: chartOptions
128
+ });
129
+ }
130
+
131
+ function startDataUpdates() {
132
+ // Update metric cards every 3 seconds
133
+ setInterval(() => {
134
+ const cards = document.querySelectorAll('metrics-card');
135
+
136
+ // Active Users
137
+ const users = 5000 + Math.floor(Math.random() * 1000 - 500);
138
+ const userChange = Math.floor(Math.random() * 10 - 5);
139
+ cards[0].setAttribute('value', users.toLocaleString());
140
+ cards[0].setAttribute('change', userChange);
141
+
142
+ // Conversion Rate
143
+ const conversion = (2 + Math.random()).toFixed(2);
144
+ const conversionChange = (Math.random() * 0.5 - 0.25).toFixed(2);
145
+ cards[1].setAttribute('value', conversion + '%');
146
+ cards[1].setAttribute('change', conversionChange);
147
+
148
+ // API Latency
149
+ const latency = Math.floor(50 + Math.random() * 50);
150
+ const latencyChange = Math.floor(Math.random() * 10 - 5);
151
+ cards[2].setAttribute('value', latency + 'ms');
152
+ cards[2].setAttribute('change', latencyChange);
153
+
154
+ // Revenue Today
155
+ const revenue = Math.floor(10000 + Math.random() * 5000);
156
+ const revenueChange = Math.floor(Math.random() * 10 - 5);
157
+ cards[3].setAttribute('value', '$' + revenue.toLocaleString());
158
+ cards[3].setAttribute('change', revenueChange);
159
+
160
+ // Update charts with new data points
161
+ updateCharts();
162
+ }, 3000);
163
+ }
164
+
165
+ function updateCharts() {
166
+ // User Chart - add new point and remove oldest
167
+ const userData = userChart.data.datasets[0].data;
168
+ userData.shift();
169
+ userData.push(Math.floor(Math.random() * 5000));
170
+ userChart.update();
171
+
172
+ // Revenue Chart - update values with slight variations
173
+ revenueChart.data.datasets[0].data = [35, 25, 20, 20].map(v => v + Math.floor(Math.random() * 10 - 5));
174
+ revenueChart.update();
175
+
176
+ // Performance Chart - update with new random values
177
+ performanceChart.data.datasets[0].data = Array(7).fill(0).map(() => 100 + Math.floor(Math.random() * 300));
178
+ performanceChart.data.datasets[1].data = Array(7).fill(0).map(() => 50 + Math.floor(Math.random() * 150));
179
+ performanceChart.update();
180
+
181
+ // Activity Chart - shift data and add new point
182
+ const activityData = activityChart.data.datasets[0].data;
183
+ activityData.shift();
184
+ const lastValue = activityData[activityData.length - 1];
185
+ activityData.push(Math.max(0, lastValue + (Math.random() * 100 - 50)));
186
+ activityChart.update();
187
+ }
188
+
189
+ // Handle dark mode changes
190
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
191
+ document.documentElement.classList.toggle('dark', e.matches);
192
+ setupCharts();
193
+ });
style.css CHANGED
@@ -1,28 +1,32 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @keyframes pulse {
2
+ 0%, 100% { opacity: 1; }
3
+ 50% { opacity: 0.5; }
4
  }
5
 
6
+ .loading {
7
+ animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
 
8
  }
9
 
10
+ .change-positive {
11
+ color: #10b981;
 
 
 
12
  }
13
 
14
+ .change-negative {
15
+ color: #ef4444;
 
 
 
 
16
  }
17
 
18
+ .change-neutral {
19
+ color: #64748b;
20
  }
21
+
22
+ .dark .change-positive {
23
+ color: #34d399;
24
+ }
25
+
26
+ .dark .change-negative {
27
+ color: #f87171;
28
+ }
29
+
30
+ .dark .change-neutral {
31
+ color: #94a3b8;
32
+ }