Spaces:
Running
Running
can this be wrapped as a react app?
Browse files- src/App.css +58 -0
- src/App.js +26 -0
- src/components/Sidebar.js +60 -0
- src/pages/Apps.js +198 -0
- src/pages/Dashboard.js +119 -0
- src/pages/Login.js +67 -0
src/App.css
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```css
|
| 2 |
+
/* Tailwind base */
|
| 3 |
+
@tailwind base;
|
| 4 |
+
@tailwind components;
|
| 5 |
+
@tailwind utilities;
|
| 6 |
+
|
| 7 |
+
/* Custom styles */
|
| 8 |
+
.sidebar {
|
| 9 |
+
transition: all 0.3s ease;
|
| 10 |
+
}
|
| 11 |
+
.card-hover:hover {
|
| 12 |
+
transform: translateY(-5px);
|
| 13 |
+
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
| 14 |
+
}
|
| 15 |
+
.active-nav-item {
|
| 16 |
+
background: rgba(124, 58, 237, 0.1);
|
| 17 |
+
border-left: 4px solid #7c3aed;
|
| 18 |
+
}
|
| 19 |
+
.app-card {
|
| 20 |
+
transition: all 0.2s ease;
|
| 21 |
+
}
|
| 22 |
+
.app-card:hover {
|
| 23 |
+
transform: translateY(-3px);
|
| 24 |
+
}
|
| 25 |
+
.login-glass {
|
| 26 |
+
background: rgba(255, 255, 255, 0.1);
|
| 27 |
+
backdrop-filter: blur(10px);
|
| 28 |
+
border-radius: 20px;
|
| 29 |
+
border: 1px solid rgba(255, 255, 255, 0.18);
|
| 30 |
+
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
| 31 |
+
}
|
| 32 |
+
.gradient-bg {
|
| 33 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 34 |
+
}
|
| 35 |
+
.file-upload {
|
| 36 |
+
border: 2px dashed #d1d5db;
|
| 37 |
+
transition: all 0.3s;
|
| 38 |
+
}
|
| 39 |
+
.file-upload:hover {
|
| 40 |
+
border-color: #7c3aed;
|
| 41 |
+
background-color: rgba(124, 58, 237, 0.05);
|
| 42 |
+
}
|
| 43 |
+
.table-row:hover {
|
| 44 |
+
background-color: rgba(124, 58, 237, 0.05);
|
| 45 |
+
}
|
| 46 |
+
.user-status-active {
|
| 47 |
+
background-color: #10b9811a;
|
| 48 |
+
color: #10b981;
|
| 49 |
+
}
|
| 50 |
+
.user-status-inactive {
|
| 51 |
+
background-color: #ef44441a;
|
| 52 |
+
color: #ef4444;
|
| 53 |
+
}
|
| 54 |
+
.user-status-pending {
|
| 55 |
+
background-color: #f59e0b1a;
|
| 56 |
+
color: #f59e0b;
|
| 57 |
+
}
|
| 58 |
+
```
|
src/App.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```javascript
|
| 2 |
+
import React from 'react';
|
| 3 |
+
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
| 4 |
+
import Dashboard from './pages/Dashboard';
|
| 5 |
+
import Apps from './pages/Apps';
|
| 6 |
+
import Users from './pages/Users';
|
| 7 |
+
import NewApp from './pages/NewApp';
|
| 8 |
+
import Login from './pages/Login';
|
| 9 |
+
import './App.css';
|
| 10 |
+
|
| 11 |
+
function App() {
|
| 12 |
+
return (
|
| 13 |
+
<Router>
|
| 14 |
+
<Routes>
|
| 15 |
+
<Route path="/" element={<Login />} />
|
| 16 |
+
<Route path="/dashboard" element={<Dashboard />} />
|
| 17 |
+
<Route path="/apps" element={<Apps />} />
|
| 18 |
+
<Route path="/users" element={<Users />} />
|
| 19 |
+
<Route path="/new-app" element={<NewApp />} />
|
| 20 |
+
</Routes>
|
| 21 |
+
</Router>
|
| 22 |
+
);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
export default App;
|
| 26 |
+
```
|
src/components/Sidebar.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```javascript
|
| 2 |
+
import React from 'react';
|
| 3 |
+
import { Link, useLocation } from 'react-router-dom';
|
| 4 |
+
|
| 5 |
+
const Sidebar = ({ active }) => {
|
| 6 |
+
const location = useLocation();
|
| 7 |
+
|
| 8 |
+
const isActive = (path) => {
|
| 9 |
+
return location.pathname === path ? 'active-nav-item text-purple-600 bg-purple-50' :
|
| 10 |
+
'text-gray-600 hover:bg-purple-50 hover:text-purple-600';
|
| 11 |
+
};
|
| 12 |
+
|
| 13 |
+
return (
|
| 14 |
+
<div className="sidebar bg-white w-64 border-r border-gray-200 flex flex-col">
|
| 15 |
+
<div className="flex items-center justify-center h-16 px-4 border-b border-gray-200">
|
| 16 |
+
<div className="flex items-center">
|
| 17 |
+
<i data-feather="box" className="w-6 h-6 text-purple-600"></i>
|
| 18 |
+
<span className="ml-2 text-xl font-bold text-gray-800">AppVoyage</span>
|
| 19 |
+
</div>
|
| 20 |
+
</div>
|
| 21 |
+
|
| 22 |
+
<div className="flex-1 overflow-y-auto">
|
| 23 |
+
<nav className="px-4 py-6 space-y-1">
|
| 24 |
+
<Link to="/dashboard" className={`group flex items-center px-3 py-3 text-sm font-medium rounded-md ${isActive('/dashboard')}`}>
|
| 25 |
+
<i data-feather="home" className={`flex-shrink-0 h-5 w-5 mr-3 ${isActive('/dashboard').includes('text-purple-600') ? 'text-purple-600' : 'text-gray-400'}`}></i>
|
| 26 |
+
Dashboard
|
| 27 |
+
</Link>
|
| 28 |
+
|
| 29 |
+
<Link to="/apps" className={`group flex items-center px-3 py-3 text-sm font-medium rounded-md ${isActive('/apps')}`}>
|
| 30 |
+
<i data-feather="grid" className={`flex-shrink-0 h-5 w-5 mr-3 ${isActive('/apps').includes('text-purple-600') ? 'text-purple-600' : 'text-gray-400'}`}></i>
|
| 31 |
+
Apps
|
| 32 |
+
</Link>
|
| 33 |
+
|
| 34 |
+
<Link to="/users" className={`group flex items-center px-3 py-3 text-sm font-medium rounded-md ${isActive('/users')}`}>
|
| 35 |
+
<i data-feather="users" className={`flex-shrink-0 h-5 w-5 mr-3 ${isActive('/users').includes('text-purple-600') ? 'text-purple-600' : 'text-gray-400'}`}></i>
|
| 36 |
+
Users
|
| 37 |
+
</Link>
|
| 38 |
+
|
| 39 |
+
<Link to="#" className="group flex items-center px-3 py-3 text-sm font-medium rounded-md text-gray-600 hover:bg-purple-50 hover:text-purple-600">
|
| 40 |
+
<i data-feather="settings" className="flex-shrink-0 h-5 w-5 mr-3 text-gray-400 group-hover:text-purple-600"></i>
|
| 41 |
+
Settings
|
| 42 |
+
</Link>
|
| 43 |
+
</nav>
|
| 44 |
+
</div>
|
| 45 |
+
|
| 46 |
+
<div className="p-4 border-t border-gray-200">
|
| 47 |
+
<div className="flex items-center">
|
| 48 |
+
<img className="h-9 w-9 rounded-full" src="http://static.photos/people/200x200/10" alt="User avatar" />
|
| 49 |
+
<div className="ml-3">
|
| 50 |
+
<p className="text-sm font-medium text-gray-700">John Doe</p>
|
| 51 |
+
<a href="#" className="text-xs font-medium text-purple-600 hover:text-purple-500">Sign out</a>
|
| 52 |
+
</div>
|
| 53 |
+
</div>
|
| 54 |
+
</div>
|
| 55 |
+
</div>
|
| 56 |
+
);
|
| 57 |
+
};
|
| 58 |
+
|
| 59 |
+
export default Sidebar;
|
| 60 |
+
```
|
src/pages/Apps.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```javascript
|
| 2 |
+
import React from 'react';
|
| 3 |
+
import { Link } from 'react-router-dom';
|
| 4 |
+
import Sidebar from '../components/Sidebar';
|
| 5 |
+
|
| 6 |
+
const Apps = () => {
|
| 7 |
+
const apps = [
|
| 8 |
+
{
|
| 9 |
+
id: 1,
|
| 10 |
+
name: "Payment Gateway",
|
| 11 |
+
type: "Web App",
|
| 12 |
+
color: "purple",
|
| 13 |
+
description: "Secure payment processing solution with multiple currency support.",
|
| 14 |
+
updated: "2 days ago",
|
| 15 |
+
image: "1"
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
id: 2,
|
| 19 |
+
name: "USSD Banking",
|
| 20 |
+
type: "USSD",
|
| 21 |
+
color: "blue",
|
| 22 |
+
description: "Mobile banking service accessible via USSD without internet.",
|
| 23 |
+
updated: "1 week ago",
|
| 24 |
+
image: "2"
|
| 25 |
+
},
|
| 26 |
+
{
|
| 27 |
+
id: 3,
|
| 28 |
+
name: "Weather API",
|
| 29 |
+
type: "API",
|
| 30 |
+
color: "green",
|
| 31 |
+
description: "Real-time weather data and forecasts API service.",
|
| 32 |
+
updated: "3 days ago",
|
| 33 |
+
image: "3"
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
id: 4,
|
| 37 |
+
name: "Inventory System",
|
| 38 |
+
type: "Web App",
|
| 39 |
+
color: "yellow",
|
| 40 |
+
description: "Comprehensive inventory management system with reporting.",
|
| 41 |
+
updated: "yesterday",
|
| 42 |
+
image: "4"
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
id: 5,
|
| 46 |
+
name: "USSD Voting",
|
| 47 |
+
type: "USSD",
|
| 48 |
+
color: "red",
|
| 49 |
+
description: "Mobile voting system accessible via USSD for elections.",
|
| 50 |
+
updated: "2 weeks ago",
|
| 51 |
+
image: "5"
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
id: 6,
|
| 55 |
+
name: "SMS Gateway API",
|
| 56 |
+
type: "API",
|
| 57 |
+
color: "indigo",
|
| 58 |
+
description: "Bulk SMS sending and receiving API with analytics.",
|
| 59 |
+
updated: "5 days ago",
|
| 60 |
+
image: "6"
|
| 61 |
+
}
|
| 62 |
+
];
|
| 63 |
+
|
| 64 |
+
return (
|
| 65 |
+
<div className="flex h-screen overflow-hidden bg-gray-50">
|
| 66 |
+
<Sidebar active="apps" />
|
| 67 |
+
|
| 68 |
+
<div className="flex-1 overflow-auto">
|
| 69 |
+
<Header title="All Apps" />
|
| 70 |
+
|
| 71 |
+
<main className="p-6">
|
| 72 |
+
<div className="mb-6 bg-white rounded-lg shadow p-4">
|
| 73 |
+
<div className="flex flex-col md:flex-row md:items-center md:justify-between">
|
| 74 |
+
<div className="relative w-full md:w-64 mb-4 md:mb-0">
|
| 75 |
+
<input type="text" placeholder="Search apps..."
|
| 76 |
+
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500" />
|
| 77 |
+
<i data-feather="search" className="absolute left-3 top-2.5 text-gray-400"></i>
|
| 78 |
+
</div>
|
| 79 |
+
|
| 80 |
+
<div className="flex space-x-2">
|
| 81 |
+
<select className="border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-purple-500">
|
| 82 |
+
<option>All Categories</option>
|
| 83 |
+
<option>Web Apps</option>
|
| 84 |
+
<option>USSD</option>
|
| 85 |
+
<option>APIs</option>
|
| 86 |
+
</select>
|
| 87 |
+
|
| 88 |
+
<select className="border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-purple-500">
|
| 89 |
+
<option>Sort by</option>
|
| 90 |
+
<option>Recently Added</option>
|
| 91 |
+
<option>Most Popular</option>
|
| 92 |
+
<option>A-Z</option>
|
| 93 |
+
</select>
|
| 94 |
+
|
| 95 |
+
<Link to="/new-app" className="bg-purple-600 text-white px-4 py-2 rounded-lg hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 flex items-center">
|
| 96 |
+
<i data-feather="plus" className="w-4 h-4 mr-1"></i>
|
| 97 |
+
New App
|
| 98 |
+
</Link>
|
| 99 |
+
</div>
|
| 100 |
+
</div>
|
| 101 |
+
</div>
|
| 102 |
+
|
| 103 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
| 104 |
+
{apps.map(app => (
|
| 105 |
+
<AppCard key={app.id} app={app} />
|
| 106 |
+
))}
|
| 107 |
+
</div>
|
| 108 |
+
|
| 109 |
+
<Pagination />
|
| 110 |
+
</main>
|
| 111 |
+
</div>
|
| 112 |
+
</div>
|
| 113 |
+
);
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
const Header = ({ title }) => (
|
| 117 |
+
<header className="bg-white shadow-sm">
|
| 118 |
+
<div className="flex justify-between items-center px-6 py-4">
|
| 119 |
+
<h1 className="text-2xl font-semibold text-gray-800">{title}</h1>
|
| 120 |
+
<div className="flex items-center space-x-4">
|
| 121 |
+
<button className="p-2 rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200">
|
| 122 |
+
<i data-feather="bell"></i>
|
| 123 |
+
</button>
|
| 124 |
+
<button className="p-2 rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200">
|
| 125 |
+
<i data-feather="help-circle"></i>
|
| 126 |
+
</button>
|
| 127 |
+
</div>
|
| 128 |
+
</div>
|
| 129 |
+
</header>
|
| 130 |
+
);
|
| 131 |
+
|
| 132 |
+
const AppCard = ({ app }) => (
|
| 133 |
+
<div className="app-card bg-white rounded-lg shadow overflow-hidden transition-all duration-200">
|
| 134 |
+
<div className={`h-40 bg-${app.color}-100 flex items-center justify-center`}>
|
| 135 |
+
<img
|
| 136 |
+
src={`http://static.photos/technology/640x360/${app.image}`}
|
| 137 |
+
alt="App screenshot"
|
| 138 |
+
className="h-full w-full object-cover"
|
| 139 |
+
/>
|
| 140 |
+
</div>
|
| 141 |
+
<div className="p-5">
|
| 142 |
+
<div className="flex justify-between items-start">
|
| 143 |
+
<div>
|
| 144 |
+
<h3 className="text-lg font-semibold text-gray-800">{app.name}</h3>
|
| 145 |
+
<span className={`inline-block px-2 py-1 text-xs font-semibold rounded-full bg-${app.color}-100 text-${app.color}-800 mt-1`}>
|
| 146 |
+
{app.type}
|
| 147 |
+
</span>
|
| 148 |
+
</div>
|
| 149 |
+
<div className="flex space-x-2">
|
| 150 |
+
<button className="p-2 text-gray-500 hover:text-purple-600 hover:bg-purple-50 rounded-full">
|
| 151 |
+
<i data-feather="edit-2" className="w-4 h-4"></i>
|
| 152 |
+
</button>
|
| 153 |
+
<button className="p-2 text-gray-500 hover:text-red-600 hover:bg-red-50 rounded-full">
|
| 154 |
+
<i data-feather="trash-2" className="w-4 h-4"></i>
|
| 155 |
+
</button>
|
| 156 |
+
</div>
|
| 157 |
+
</div>
|
| 158 |
+
<p className="mt-3 text-gray-600 text-sm">{app.description}</p>
|
| 159 |
+
<div className="mt-4 flex justify-between items-center">
|
| 160 |
+
<span className="text-xs text-gray-500">Updated {app.updated}</span>
|
| 161 |
+
<a
|
| 162 |
+
href={app.type === 'USSD' ? `tel:*123#` : `https://${app.name.toLowerCase()}.example.com`}
|
| 163 |
+
target="_blank"
|
| 164 |
+
rel="noopener noreferrer"
|
| 165 |
+
className={`text-sm font-medium text-${app.color}-600 hover:text-${app.color}-500 flex items-center`}
|
| 166 |
+
>
|
| 167 |
+
{app.type === 'USSD' ? 'Dial' : app.type === 'API' ? 'Docs' : 'Open'}
|
| 168 |
+
<i data-feather={app.type === 'USSD' ? 'phone' : app.type === 'API' ? 'file-text' : 'external-link'} className="w-4 h-4 ml-1"></i>
|
| 169 |
+
</a>
|
| 170 |
+
</div>
|
| 171 |
+
</div>
|
| 172 |
+
</div>
|
| 173 |
+
);
|
| 174 |
+
|
| 175 |
+
const Pagination = () => (
|
| 176 |
+
<div className="mt-8 flex justify-center">
|
| 177 |
+
<nav className="inline-flex rounded-md shadow">
|
| 178 |
+
<a href="#" className="px-3 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
| 179 |
+
Previous
|
| 180 |
+
</a>
|
| 181 |
+
<a href="#" className="px-3 py-2 border-t border-b border-gray-300 bg-white text-sm font-medium text-purple-600 hover:bg-purple-50">
|
| 182 |
+
1
|
| 183 |
+
</a>
|
| 184 |
+
<a href="#" className="px-3 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
| 185 |
+
2
|
| 186 |
+
</a>
|
| 187 |
+
<a href="#" className="px-3 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
| 188 |
+
3
|
| 189 |
+
</a>
|
| 190 |
+
<a href="#" className="px-3 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
|
| 191 |
+
Next
|
| 192 |
+
</a>
|
| 193 |
+
</nav>
|
| 194 |
+
</div>
|
| 195 |
+
);
|
| 196 |
+
|
| 197 |
+
export default Apps;
|
| 198 |
+
```
|
src/pages/Dashboard.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```javascript
|
| 2 |
+
import React from 'react';
|
| 3 |
+
import { Link } from 'react-router-dom';
|
| 4 |
+
import Sidebar from '../components/Sidebar';
|
| 5 |
+
|
| 6 |
+
const Dashboard = () => {
|
| 7 |
+
return (
|
| 8 |
+
<div className="flex h-screen overflow-hidden bg-gray-50">
|
| 9 |
+
<Sidebar active="dashboard" />
|
| 10 |
+
|
| 11 |
+
<div className="flex-1 overflow-auto">
|
| 12 |
+
<Header title="Dashboard" />
|
| 13 |
+
|
| 14 |
+
<main className="p-6">
|
| 15 |
+
{/* Metrics */}
|
| 16 |
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
| 17 |
+
{/* Metric cards */}
|
| 18 |
+
<MetricCard
|
| 19 |
+
icon="box"
|
| 20 |
+
color="purple"
|
| 21 |
+
title="Total Apps"
|
| 22 |
+
value="42"
|
| 23 |
+
/>
|
| 24 |
+
<MetricCard
|
| 25 |
+
icon="globe"
|
| 26 |
+
color="blue"
|
| 27 |
+
title="Web Apps"
|
| 28 |
+
value="28"
|
| 29 |
+
/>
|
| 30 |
+
<MetricCard
|
| 31 |
+
icon="smartphone"
|
| 32 |
+
color="green"
|
| 33 |
+
title="USSD"
|
| 34 |
+
value="9"
|
| 35 |
+
/>
|
| 36 |
+
<MetricCard
|
| 37 |
+
icon="code"
|
| 38 |
+
color="yellow"
|
| 39 |
+
title="APIs"
|
| 40 |
+
value="5"
|
| 41 |
+
/>
|
| 42 |
+
</div>
|
| 43 |
+
|
| 44 |
+
{/* Recent Activity */}
|
| 45 |
+
<div className="bg-white rounded-lg shadow overflow-hidden">
|
| 46 |
+
<div className="px-6 py-4 border-b border-gray-200">
|
| 47 |
+
<h2 className="text-lg font-semibold text-gray-800">Recent Activity</h2>
|
| 48 |
+
</div>
|
| 49 |
+
<ActivityItem
|
| 50 |
+
name="Sarah Johnson"
|
| 51 |
+
avatar="1"
|
| 52 |
+
action="Added new API integration"
|
| 53 |
+
time="10:30 AM"
|
| 54 |
+
/>
|
| 55 |
+
<ActivityItem
|
| 56 |
+
name="Mike Chen"
|
| 57 |
+
avatar="2"
|
| 58 |
+
action="Updated payment gateway app"
|
| 59 |
+
time="9:15 AM"
|
| 60 |
+
/>
|
| 61 |
+
<ActivityItem
|
| 62 |
+
name="Alex Taylor"
|
| 63 |
+
avatar="3"
|
| 64 |
+
action="Deployed new USSD service"
|
| 65 |
+
time="8:45 AM"
|
| 66 |
+
/>
|
| 67 |
+
</div>
|
| 68 |
+
</main>
|
| 69 |
+
</div>
|
| 70 |
+
</div>
|
| 71 |
+
);
|
| 72 |
+
};
|
| 73 |
+
|
| 74 |
+
const Header = ({ title }) => (
|
| 75 |
+
<header className="bg-white shadow-sm">
|
| 76 |
+
<div className="flex justify-between items-center px-6 py-4">
|
| 77 |
+
<h1 className="text-2xl font-semibold text-gray-800">{title}</h1>
|
| 78 |
+
<div className="flex items-center space-x-4">
|
| 79 |
+
<button className="p-2 rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200">
|
| 80 |
+
<i data-feather="bell"></i>
|
| 81 |
+
</button>
|
| 82 |
+
<button className="p-2 rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200">
|
| 83 |
+
<i data-feather="help-circle"></i>
|
| 84 |
+
</button>
|
| 85 |
+
</div>
|
| 86 |
+
</div>
|
| 87 |
+
</header>
|
| 88 |
+
);
|
| 89 |
+
|
| 90 |
+
const MetricCard = ({ icon, color, title, value }) => (
|
| 91 |
+
<div className="bg-white rounded-lg shadow p-6">
|
| 92 |
+
<div className="flex items-center">
|
| 93 |
+
<div className={`p-3 rounded-full bg-${color}-100 text-${color}-600`}>
|
| 94 |
+
<i data-feather={icon} className="w-6 h-6"></i>
|
| 95 |
+
</div>
|
| 96 |
+
<div className="ml-4">
|
| 97 |
+
<p className="text-sm font-medium text-gray-500">{title}</p>
|
| 98 |
+
<p className="text-2xl font-semibold text-gray-800">{value}</p>
|
| 99 |
+
</div>
|
| 100 |
+
</div>
|
| 101 |
+
</div>
|
| 102 |
+
);
|
| 103 |
+
|
| 104 |
+
const ActivityItem = ({ name, avatar, action, time }) => (
|
| 105 |
+
<div className="p-6 flex items-start">
|
| 106 |
+
<img
|
| 107 |
+
className="h-10 w-10 rounded-full"
|
| 108 |
+
src={`http://static.photos/people/200x200/${avatar}`}
|
| 109 |
+
alt="User avatar"
|
| 110 |
+
/>
|
| 111 |
+
<div className="ml-4">
|
| 112 |
+
<p className="text-sm font-medium text-gray-800">{name}</p>
|
| 113 |
+
<p className="text-sm text-gray-500">{action} at {time}</p>
|
| 114 |
+
</div>
|
| 115 |
+
</div>
|
| 116 |
+
);
|
| 117 |
+
|
| 118 |
+
export default Dashboard;
|
| 119 |
+
```
|
src/pages/Login.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```javascript
|
| 2 |
+
import React from 'react';
|
| 3 |
+
import { useNavigate } from 'react-router-dom';
|
| 4 |
+
|
| 5 |
+
const Login = () => {
|
| 6 |
+
const navigate = useNavigate();
|
| 7 |
+
|
| 8 |
+
const handleSubmit = (e) => {
|
| 9 |
+
e.preventDefault();
|
| 10 |
+
// Simulate successful login
|
| 11 |
+
navigate('/dashboard');
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
+
return (
|
| 15 |
+
<div className="gradient-bg min-h-screen flex items-center justify-center p-4">
|
| 16 |
+
<div className="login-glass p-8 max-w-md w-full">
|
| 17 |
+
<div className="text-center mb-8">
|
| 18 |
+
<i data-feather="box" className="w-12 h-12 text-white mx-auto"></i>
|
| 19 |
+
<h1 className="text-3xl font-bold text-white mt-4">AppVoyage</h1>
|
| 20 |
+
<p className="text-white opacity-80">Discover & manage your digital tools</p>
|
| 21 |
+
</div>
|
| 22 |
+
|
| 23 |
+
<form onSubmit={handleSubmit} className="space-y-6">
|
| 24 |
+
<div>
|
| 25 |
+
<label htmlFor="email" className="block text-sm font-medium text-white">Email</label>
|
| 26 |
+
<div className="mt-1 relative">
|
| 27 |
+
<input id="email" name="email" type="email" required
|
| 28 |
+
className="py-3 px-4 block w-full bg-white bg-opacity-20 border border-white border-opacity-30 rounded-lg text-white placeholder-white placeholder-opacity-70 focus:outline-none focus:ring-2 focus:ring-white focus:border-transparent" />
|
| 29 |
+
<i data-feather="mail" className="absolute right-3 top-3 text-white opacity-70"></i>
|
| 30 |
+
</div>
|
| 31 |
+
</div>
|
| 32 |
+
|
| 33 |
+
<div>
|
| 34 |
+
<label htmlFor="password" className="block text-sm font-medium text-white">Password</label>
|
| 35 |
+
<div className="mt-1 relative">
|
| 36 |
+
<input id="password" name="password" type="password" required
|
| 37 |
+
className="py-3 px-4 block w-full bg-white bg-opacity-20 border border-white border-opacity-30 rounded-lg text-white placeholder-white placeholder-opacity-70 focus:outline-none focus:ring-2 focus:ring-white focus:border-transparent" />
|
| 38 |
+
<i data-feather="lock" className="absolute right-3 top-3 text-white opacity-70"></i>
|
| 39 |
+
</div>
|
| 40 |
+
</div>
|
| 41 |
+
|
| 42 |
+
<div className="flex items-center justify-between">
|
| 43 |
+
<div className="flex items-center">
|
| 44 |
+
<input id="remember-me" name="remember-me" type="checkbox"
|
| 45 |
+
className="h-4 w-4 bg-white bg-opacity-20 border border-white rounded focus:ring-white" />
|
| 46 |
+
<label htmlFor="remember-me" className="ml-2 block text-sm text-white">Remember me</label>
|
| 47 |
+
</div>
|
| 48 |
+
|
| 49 |
+
<div className="text-sm">
|
| 50 |
+
<a href="#" className="font-medium text-white hover:text-opacity-80">Forgot password?</a>
|
| 51 |
+
</div>
|
| 52 |
+
</div>
|
| 53 |
+
|
| 54 |
+
<div>
|
| 55 |
+
<button type="submit"
|
| 56 |
+
className="w-full flex justify-center py-3 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-purple-700 bg-white hover:bg-opacity-90 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-white transition-transform hover:scale-105">
|
| 57 |
+
Sign in
|
| 58 |
+
</button>
|
| 59 |
+
</div>
|
| 60 |
+
</form>
|
| 61 |
+
</div>
|
| 62 |
+
</div>
|
| 63 |
+
);
|
| 64 |
+
};
|
| 65 |
+
|
| 66 |
+
export default Login;
|
| 67 |
+
```
|