Spaces:
Running
That looks like a design for a full-stack web application. Based on the uploaded diagrams, the app called https://www.Godsrods.online appears to use a client-server architecture to manage user logins, display a front-end interface, handle backend logic, and interact with a database.
Browse filesHere is an explanation of how the application would function, broken down by component.
💻 Application Functionality Overview
The app's function revolves around the interaction between the Front End (Client), the Back End (Server), and the SQL Database.
1. Front End (Client-Side)
The Front End is what the user sees and interacts with in their web browser (the pink box labeled "Front End"). It handles the user interface and presentation.
* HTML: Provides the basic structure and content of the web pages.
* Py (likely Python for the browser, or potentially a Python framework like Flask/Django that renders pages): While Python is typically a backend language, its inclusion here might suggest using a framework that helps render dynamic HTML or an advanced client-side library (though less common than JavaScript).
* Jsx (JavaScript XML): This is a syntax extension often used with React to describe the structure of the UI. It suggests the front end is a modern Single Page Application (SPA) that handles dynamic content updates.
* .glb (GLB File): This file type is a binary format for 3D models (GL Transmission Format). Its inclusion strongly suggests the app displays interactive 3D graphics (e.g., cars or models, given the domain name "Godsrods").
2. User Interaction and Data Flow
* Homepage Access: A user navigates to the URL. The browser requests the necessary HTML, Jsx, and Python/JS files from the server.
* User Login: The user enters credentials, which the front end sends to the Express.js (Backend).
* Data Request: The backend validates the credentials against the SQL Database information (which includes User Password).
* Session Establishment: Upon successful login, the backend sends session information back to the front end. The front end then displays the Username and uses the returned variables, such as GTR Tokens Balance and Cash Balance, to personalize the user's view.
3. Back End (Server-Side)
The Express.js (Backend) component (Orange box) acts as the brain, processing user requests and communicating with the database.
* API Endpoints: The front end sends requests (like login attempts, retrieving a listing, or using an admin control) to the Express.js server.
* Business Logic: Express.js contains the code to implement the app's features, such as:
* Authentication/Authorization: Checking the validity of user logins and determining if a user has Admin Privileges.
* Data Manipulation: Performing the actions requested by the user or admin, which often involves querying or modifying the database.
4. SQL Database
The SQL Database (Blue box) is the persistent data storage for the entire application.
* Variables Storage: Stores user-specific data like GTR Tokens Balance, Username, and User Password.
* Database Objects (admin): These objects define the structure and the available manipulation actions for the core data, likely related to a car marketplace. The listed actions include:
* Rename String to Car/User
* Add/Remove listing
* Marketplace table fields: listing.car.marketplace.price.date.sale.date, listing.car.marketplace.price.date.sale.price, etc.
* Admin Controls: The database is where the logic and permissions for Drag and Drop GUI and defining Admin Privileges are stored and managed, allowing administrators to modify the application's content or structure.
The overall function is to provide a dynamic marketplace experience, potentially involving 3D models (.glb), where users can log in and view listings managed by an administrator through a specific interface.
Would you like me to suggest specific technologies (like React, Node.js) that are commonly associated with this kind of design?
- admin.html +94 -0
- components/car-card.js +9 -5
- components/navbar.js +11 -5
- script.js +153 -38
- style.css +8 -0
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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>Admin Panel | GodsRods</title>
|
| 7 |
+
<link rel="stylesheet" href="style.css">
|
| 8 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 10 |
+
<script src="components/navbar.js"></script>
|
| 11 |
+
<script src="components/footer.js"></script>
|
| 12 |
+
</head>
|
| 13 |
+
<body class="bg-gray-100">
|
| 14 |
+
<custom-navbar username="Admin"></custom-navbar>
|
| 15 |
+
|
| 16 |
+
<main class="container mx-auto px-4 py-8">
|
| 17 |
+
<div class="bg-white rounded-xl shadow-md p-6 mb-8">
|
| 18 |
+
<h1 class="text-3xl font-bold text-gray-800 mb-6">Admin Dashboard</h1>
|
| 19 |
+
|
| 20 |
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
| 21 |
+
<div class="bg-blue-50 p-4 rounded-lg">
|
| 22 |
+
<h3 class="text-lg font-semibold text-blue-800 mb-2">Total Listings</h3>
|
| 23 |
+
<p class="text-2xl font-bold" id="total-listings">0</p>
|
| 24 |
+
</div>
|
| 25 |
+
<div class="bg-green-50 p-4 rounded-lg">
|
| 26 |
+
<h3 class="text-lg font-semibold text-green-800 mb-2">Active Users</h3>
|
| 27 |
+
<p class="text-2xl font-bold" id="active-users">0</p>
|
| 28 |
+
</div>
|
| 29 |
+
<div class="bg-purple-50 p-4 rounded-lg">
|
| 30 |
+
<h3 class="text-lg font-semibold text-purple-800 mb-2">Revenue (7d)</h3>
|
| 31 |
+
<p class="text-2xl font-bold" id="weekly-revenue">$0</p>
|
| 32 |
+
</div>
|
| 33 |
+
</div>
|
| 34 |
+
|
| 35 |
+
<h2 class="text-2xl font-bold text-gray-800 mb-4">Manage Listings</h2>
|
| 36 |
+
<div class="overflow-x-auto">
|
| 37 |
+
<table class="min-w-full bg-white rounded-lg overflow-hidden">
|
| 38 |
+
<thead class="bg-gray-100">
|
| 39 |
+
<tr>
|
| 40 |
+
<th class="py-3 px-4 text-left">ID</th>
|
| 41 |
+
<th class="py-3 px-4 text-left">Model</th>
|
| 42 |
+
<th class="py-3 px-4 text-left">Price</th>
|
| 43 |
+
<th class="py-3 px-4 text-left">Tokens</th>
|
| 44 |
+
<th class="py-3 px-4 text-left">Status</th>
|
| 45 |
+
<th class="py-3 px-4 text-left">Actions</th>
|
| 46 |
+
</tr>
|
| 47 |
+
</thead>
|
| 48 |
+
<tbody id="listings-table" class="divide-y divide-gray-200">
|
| 49 |
+
<!-- Listings will be loaded here -->
|
| 50 |
+
</tbody>
|
| 51 |
+
</table>
|
| 52 |
+
</div>
|
| 53 |
+
|
| 54 |
+
<div class="mt-8">
|
| 55 |
+
<h2 class="text-2xl font-bold text-gray-800 mb-4">Add New Listing</h2>
|
| 56 |
+
<form id="add-listing-form" class="space-y-4">
|
| 57 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
| 58 |
+
<div>
|
| 59 |
+
<label class="block text-gray-700 mb-2">Model Name</label>
|
| 60 |
+
<input type="text" name="title" class="w-full px-4 py-2 border rounded-lg" required>
|
| 61 |
+
</div>
|
| 62 |
+
<div>
|
| 63 |
+
<label class="block text-gray-700 mb-2">Price ($)</label>
|
| 64 |
+
<input type="number" name="price" class="w-full px-4 py-2 border rounded-lg" required>
|
| 65 |
+
</div>
|
| 66 |
+
<div>
|
| 67 |
+
<label class="block text-gray-700 mb-2">Token Cost</label>
|
| 68 |
+
<input type="number" name="tokens" class="w-full px-4 py-2 border rounded-lg" required>
|
| 69 |
+
</div>
|
| 70 |
+
<div>
|
| 71 |
+
<label class="block text-gray-700 mb-2">Model URL (.glb)</label>
|
| 72 |
+
<input type="text" name="modelUrl" class="w-full px-4 py-2 border rounded-lg" required>
|
| 73 |
+
</div>
|
| 74 |
+
<div class="md:col-span-2">
|
| 75 |
+
<label class="block text-gray-700 mb-2">Preview Image URL</label>
|
| 76 |
+
<input type="text" name="imageUrl" class="w-full px-4 py-2 border rounded-lg" required>
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
+
<button type="submit" class="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700 transition">
|
| 80 |
+
Add Listing
|
| 81 |
+
</button>
|
| 82 |
+
</form>
|
| 83 |
+
</div>
|
| 84 |
+
</div>
|
| 85 |
+
</main>
|
| 86 |
+
|
| 87 |
+
<custom-footer></custom-footer>
|
| 88 |
+
|
| 89 |
+
<script>
|
| 90 |
+
feather.replace();
|
| 91 |
+
</script>
|
| 92 |
+
<script src="script.js"></script>
|
| 93 |
+
</body>
|
| 94 |
+
</html>
|
|
@@ -43,11 +43,15 @@ class CustomCarCard extends HTMLElement {
|
|
| 43 |
${tokens} GTR
|
| 44 |
</div>
|
| 45 |
</div>
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
</div>
|
| 52 |
</a>
|
| 53 |
`;
|
|
|
|
| 43 |
${tokens} GTR
|
| 44 |
</div>
|
| 45 |
</div>
|
| 46 |
+
<div class="flex items-center">
|
| 47 |
+
<button class="text-blue-600 hover:text-blue-800 transition mr-2">
|
| 48 |
+
<i data-feather="eye"></i>
|
| 49 |
+
</button>
|
| 50 |
+
<button class="text-green-600 hover:text-green-800 transition">
|
| 51 |
+
<i data-feather="shopping-cart"></i>
|
| 52 |
+
</button>
|
| 53 |
+
</div>
|
| 54 |
+
</div>
|
| 55 |
</div>
|
| 56 |
</a>
|
| 57 |
`;
|
|
@@ -49,11 +49,17 @@ class CustomNavbar extends HTMLElement {
|
|
| 49 |
<!-- User Menu -->
|
| 50 |
<div class="auth-only relative">
|
| 51 |
<button id="user-menu-button" class="flex items-center space-x-2 focus:outline-none">
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
<div id="user-menu" class="user-menu absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
|
| 59 |
<a href="/profile" class="block px-4 py-2 text-gray-700 hover:bg-gray-100">Profile</a>
|
|
|
|
| 49 |
<!-- User Menu -->
|
| 50 |
<div class="auth-only relative">
|
| 51 |
<button id="user-menu-button" class="flex items-center space-x-2 focus:outline-none">
|
| 52 |
+
<div class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center">
|
| 53 |
+
<i data-feather="user" class="text-blue-600 w-4 h-4"></i>
|
| 54 |
+
</div>
|
| 55 |
+
<div class="text-left">
|
| 56 |
+
<span class="text-gray-700 font-medium block">${this.getAttribute('username') || 'Account'}</span>
|
| 57 |
+
<span class="user-balance text-xs text-gray-500 block">
|
| 58 |
+
<span class="user-tokens">0 GTR</span> |
|
| 59 |
+
<span class="user-cash">$0</span>
|
| 60 |
+
</span>
|
| 61 |
+
</div>
|
| 62 |
+
</button>
|
| 63 |
|
| 64 |
<div id="user-menu" class="user-menu absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1 z-50">
|
| 65 |
<a href="/profile" class="block px-4 py-2 text-gray-700 hover:bg-gray-100">Profile</a>
|
|
@@ -1,51 +1,166 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
});
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
});
|
|
|
|
| 19 |
|
| 20 |
-
function
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
if (isLoggedIn) {
|
| 26 |
-
document.querySelectorAll('.auth-only').forEach(el => {
|
| 27 |
-
el.classList.remove('hidden');
|
| 28 |
});
|
| 29 |
-
document.querySelectorAll('.
|
| 30 |
-
el.
|
| 31 |
});
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
});
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
}
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
// Global state
|
| 3 |
+
let currentUser = null;
|
| 4 |
+
const API_BASE_URL = 'https://api.godsrods.online';
|
| 5 |
+
|
| 6 |
+
// Initialize app
|
| 7 |
+
document.addEventListener('DOMContentLoaded', async function() {
|
| 8 |
+
await checkAuthStatus();
|
| 9 |
+
loadMarketplaceData();
|
| 10 |
+
init3DViewers();
|
| 11 |
+
});
|
| 12 |
+
|
| 13 |
+
async function checkAuthStatus() {
|
| 14 |
+
try {
|
| 15 |
+
const response = await fetch(`${API_BASE_URL}/auth/status`, {
|
| 16 |
+
credentials: 'include'
|
| 17 |
});
|
| 18 |
+
|
| 19 |
+
if (response.ok) {
|
| 20 |
+
const data = await response.json();
|
| 21 |
+
currentUser = data.user;
|
| 22 |
+
updateUIAuthState(true);
|
| 23 |
+
updateUserBalance(data.balance, data.tokens);
|
| 24 |
+
} else {
|
| 25 |
+
updateUIAuthState(false);
|
| 26 |
+
}
|
| 27 |
+
} catch (error) {
|
| 28 |
+
console.error('Auth check failed:', error);
|
| 29 |
+
updateUIAuthState(false);
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
function updateUIAuthState(isLoggedIn) {
|
| 34 |
+
const authElements = document.querySelectorAll('.auth-only');
|
| 35 |
+
const guestElements = document.querySelectorAll('.guest-only');
|
| 36 |
+
|
| 37 |
+
authElements.forEach(el => {
|
| 38 |
+
el.classList.toggle('hidden', !isLoggedIn);
|
| 39 |
});
|
| 40 |
|
| 41 |
+
guestElements.forEach(el => {
|
| 42 |
+
el.classList.toggle('hidden', isLoggedIn);
|
| 43 |
+
});
|
| 44 |
+
}
|
| 45 |
|
| 46 |
+
function updateUserBalance(cash, tokens) {
|
| 47 |
+
if (currentUser) {
|
| 48 |
+
document.querySelectorAll('.user-cash').forEach(el => {
|
| 49 |
+
el.textContent = `${cash.toLocaleString()}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
});
|
| 51 |
+
document.querySelectorAll('.user-tokens').forEach(el => {
|
| 52 |
+
el.textContent = `${tokens.toLocaleString()} GTR`;
|
| 53 |
});
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
async function loadMarketplaceData() {
|
| 58 |
+
try {
|
| 59 |
+
const response = await fetch(`${API_BASE_URL}/marketplace`);
|
| 60 |
+
if (response.ok) {
|
| 61 |
+
const data = await response.json();
|
| 62 |
+
renderCarListings(data.listings);
|
| 63 |
+
}
|
| 64 |
+
} catch (error) {
|
| 65 |
+
console.error('Failed to load marketplace data:', error);
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
function renderCarListings(listings) {
|
| 70 |
+
const container = document.querySelector('#listings-container') ||
|
| 71 |
+
document.querySelector('.grid.grid-cols-1.md\\:grid-cols-2.lg\\:grid-cols-3.gap-6');
|
| 72 |
+
|
| 73 |
+
if (container) {
|
| 74 |
+
container.innerHTML = '';
|
| 75 |
+
listings.forEach(listing => {
|
| 76 |
+
const card = document.createElement('custom-car-card');
|
| 77 |
+
card.setAttribute('title', listing.title);
|
| 78 |
+
card.setAttribute('price', listing.price);
|
| 79 |
+
card.setAttribute('tokens', listing.tokenCost);
|
| 80 |
+
card.setAttribute('image', listing.imageUrl);
|
| 81 |
+
card.setAttribute('model', listing.modelUrl);
|
| 82 |
+
card.setAttribute('id', `car-${listing.id}`);
|
| 83 |
+
container.appendChild(card);
|
| 84 |
});
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
function init3DViewers() {
|
| 89 |
+
// Initialize 3D model viewers
|
| 90 |
+
document.addEventListener('click', function(e) {
|
| 91 |
+
if (e.target.closest('custom-car-card')) {
|
| 92 |
+
const card = e.target.closest('custom-car-card');
|
| 93 |
+
const modelUrl = card.getAttribute('model');
|
| 94 |
+
openModelViewer(modelUrl);
|
| 95 |
+
}
|
| 96 |
+
});
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
function openModelViewer(modelUrl) {
|
| 100 |
+
// This would show a modal with the 3D viewer
|
| 101 |
+
console.log('Opening 3D viewer for:', modelUrl);
|
| 102 |
+
// Implementation would use model-viewer or three.js
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
// Auth functions
|
| 106 |
+
async function handleLogin(email, password) {
|
| 107 |
+
try {
|
| 108 |
+
const response = await fetch(`${API_BASE_URL}/auth/login`, {
|
| 109 |
+
method: 'POST',
|
| 110 |
+
headers: { 'Content-Type': 'application/json' },
|
| 111 |
+
body: JSON.stringify({ email, password }),
|
| 112 |
+
credentials: 'include'
|
| 113 |
});
|
| 114 |
+
|
| 115 |
+
if (response.ok) {
|
| 116 |
+
const data = await response.json();
|
| 117 |
+
currentUser = data.user;
|
| 118 |
+
updateUIAuthState(true);
|
| 119 |
+
updateUserBalance(data.balance, data.tokens);
|
| 120 |
+
return { success: true };
|
| 121 |
+
} else {
|
| 122 |
+
return { success: false, error: 'Invalid credentials' };
|
| 123 |
+
}
|
| 124 |
+
} catch (error) {
|
| 125 |
+
console.error('Login failed:', error);
|
| 126 |
+
return { success: false, error: 'Network error' };
|
| 127 |
}
|
| 128 |
}
|
| 129 |
|
| 130 |
+
async function handleLogout() {
|
| 131 |
+
try {
|
| 132 |
+
await fetch(`${API_BASE_URL}/auth/logout`, {
|
| 133 |
+
method: 'POST',
|
| 134 |
+
credentials: 'include'
|
| 135 |
+
});
|
| 136 |
+
currentUser = null;
|
| 137 |
+
updateUIAuthState(false);
|
| 138 |
+
} catch (error) {
|
| 139 |
+
console.error('Logout failed:', error);
|
| 140 |
+
}
|
| 141 |
}
|
| 142 |
|
| 143 |
+
// Admin functions
|
| 144 |
+
async function adminAddListing(listingData) {
|
| 145 |
+
if (!currentUser?.isAdmin) return;
|
| 146 |
+
|
| 147 |
+
try {
|
| 148 |
+
const response = await fetch(`${API_BASE_URL}/admin/listings`, {
|
| 149 |
+
method: 'POST',
|
| 150 |
+
headers: {
|
| 151 |
+
'Content-Type': 'application/json',
|
| 152 |
+
'Authorization': `Bearer ${currentUser.token}`
|
| 153 |
+
},
|
| 154 |
+
body: JSON.stringify(listingData)
|
| 155 |
+
});
|
| 156 |
+
|
| 157 |
+
if (response.ok) {
|
| 158 |
+
loadMarketplaceData();
|
| 159 |
+
return true;
|
| 160 |
+
}
|
| 161 |
+
return false;
|
| 162 |
+
} catch (error) {
|
| 163 |
+
console.error('Failed to add listing:', error);
|
| 164 |
+
return false;
|
| 165 |
+
}
|
| 166 |
+
}
|
|
@@ -1,4 +1,12 @@
|
|
|
|
|
| 1 |
/* Custom styles that extend Tailwind */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 3 |
|
| 4 |
body {
|
|
|
|
| 1 |
+
|
| 2 |
/* Custom styles that extend Tailwind */
|
| 3 |
+
.admin-only {
|
| 4 |
+
display: none;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
.auth-only .admin-only {
|
| 8 |
+
display: block;
|
| 9 |
+
}
|
| 10 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
| 11 |
|
| 12 |
body {
|