هذا الالتزام موجود في:
Mohamed Moustafa
2026-07-27 23:27:12 +03:00
التزام c653222887
43 ملفات معدلة مع 3837 إضافات و0 حذوفات

ثنائية
q1-deploy-monitor/public/.DS_Store مباع Normal file

ملف ثنائي غير معروض.

عرض الملف

@@ -0,0 +1,137 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #0f172a;
color: #e2e8f0;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.dashboard {
width: 100%;
max-width: 800px;
padding: 40px 20px;
}
.header {
text-align: center;
margin-bottom: 48px;
}
.header h1 {
font-size: 1.75rem;
font-weight: 600;
letter-spacing: -0.025em;
margin-bottom: 8px;
}
.update-time {
font-size: 0.85rem;
color: #64748b;
}
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.card {
background: #1e293b;
border: 1px solid #334155;
border-radius: 16px;
padding: 28px 24px;
text-align: center;
transition: border-color 0.3s, box-shadow 0.3s;
}
.card:hover {
border-color: #475569;
}
.card-label {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #94a3b8;
margin-bottom: 12px;
}
.card-value {
font-size: 2rem;
font-weight: 700;
letter-spacing: -0.025em;
}
.card.status-up {
border-color: #22c55e;
box-shadow: 0 0 20px rgba(34, 197, 94, 0.1);
}
.card.status-up .card-value {
color: #4ade80;
}
.card.status-down {
border-color: #ef4444;
box-shadow: 0 0 20px rgba(239, 68, 68, 0.1);
}
.card.status-down .card-value {
color: #f87171;
}
.card.response .card-value {
color: #38bdf8;
}
.card.requests .card-value {
color: #c084fc;
}
.pulse-container {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin-top: 36px;
}
.pulse {
width: 10px;
height: 10px;
border-radius: 50%;
background: #22c55e;
animation: pulse 2s ease-in-out infinite;
}
.pulse.offline {
background: #ef4444;
}
.pulse-label {
font-size: 0.85rem;
color: #64748b;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(0.8); }
}
@media (max-width: 640px) {
.cards {
grid-template-columns: 1fr;
}
.card-value {
font-size: 1.5rem;
}
}

عرض الملف

@@ -0,0 +1,41 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ghaymah Monitoring Dashboard</title>
<link rel="stylesheet" href="dashboard.css">
</head>
<body>
<div class="dashboard">
<header class="header">
<h1>Ghaymah Monitoring Dashboard</h1>
<span class="update-time" id="updateTime">Last update: --</span>
</header>
<div class="cards">
<div class="card" id="statusCard">
<div class="card-label">Application Status</div>
<div class="card-value" id="appStatus">--</div>
</div>
<div class="card" id="responseCard">
<div class="card-label">Response Time</div>
<div class="card-value" id="responseTime">-- ms</div>
</div>
<div class="card" id="requestsCard">
<div class="card-label">Total Requests</div>
<div class="card-value" id="requestCount">--</div>
</div>
</div>
<div class="pulse-container" id="pulseContainer">
<div class="pulse"></div>
<span class="pulse-label">Live</span>
</div>
</div>
<script src="dashboard.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,54 @@
const STATUS_ENDPOINT = '/health';
const POLL_INTERVAL = 2000;
const statusCard = document.getElementById('statusCard');
const responseCard = document.getElementById('responseCard');
const requestsCard = document.getElementById('requestsCard');
const appStatus = document.getElementById('appStatus');
const responseTime = document.getElementById('responseTime');
const requestCount = document.getElementById('requestCount');
const updateTime = document.getElementById('updateTime');
const pulse = document.querySelector('.pulse');
function setStatusCard(status) {
statusCard.classList.remove('status-up', 'status-down');
statusCard.classList.add(status === 'UP' ? 'status-up' : 'status-down');
appStatus.textContent = status;
}
function formatTimestamp(date) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
const h = String(date.getHours()).padStart(2, '0');
const min = String(date.getMinutes()).padStart(2, '0');
const s = String(date.getSeconds()).padStart(2, '0');
return `${y}-${m}-${d} ${h}:${min}:${s}`;
}
async function poll() {
try {
const start = performance.now();
const res = await fetch(STATUS_ENDPOINT);
const elapsed = Math.round(performance.now() - start);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
setStatusCard(data.status || 'UP');
responseTime.textContent = `${elapsed} ms`;
requestCount.textContent = data.requestCount ?? '--';
pulse.classList.remove('offline');
} catch {
setStatusCard('DOWN');
responseTime.textContent = '-- ms';
requestCount.textContent = '--';
pulse.classList.add('offline');
}
updateTime.textContent = `Last update: ${formatTimestamp(new Date())}`;
}
poll();
setInterval(poll, POLL_INTERVAL);

عرض الملف

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Team Availability</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<div class="header">
<h2>Team Availability</h2>
<div class="buttons">
<button id="saveBtn">Save</button>
</div>
</div>
<div>Click save after updating each week separately</div>
<table id="scheduleTable">
<thead>
<tr>
<th>Name</th>
<th>Week</th>
<th>Mon</th>
<th>Tue</th>
<th>Wed</th>
<th>Thu</th>
<th>Fri</th>
<th>Sat</th>
<th>Sun</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
</div>
<script src="script.js"></script>
</body>
</html>

عرض الملف

@@ -0,0 +1,143 @@
let namesData = [];
let weeksData = [];
let statusesData = [];
let historyData = {};
function createDropdown(options, selectedValue = "") {
const select = document.createElement("select");
options.forEach(opt => {
const option = document.createElement("option");
option.value = opt;
option.textContent = opt;
if (opt === selectedValue) {
option.selected = true;
}
select.appendChild(option);
});
return select;
}
function applyStatusColor(select) {
select.className = 'status-select'; // Reset classes
const selectedStatus = select.value;
select.classList.add(`status-${selectedStatus}`);
}
function renderTable() {
const tableBody = document.getElementById("tableBody");
tableBody.innerHTML = "";
namesData.sort((a, b) => a.name.localeCompare(b.name));
namesData.forEach((emp, index) => {
const row = document.createElement("tr");
row.dataset.empId = emp.id;
row.classList.add(index % 2 === 0 ? "even-row" : "odd-row");
// Name cell
const nameCell = document.createElement("td");
nameCell.textContent = emp.name;
row.appendChild(nameCell);
// Week cell
const weekCell = document.createElement("td");
const defaultWeek = Object.keys(historyData[emp.id] || {})[0] || weeksData[0];
const weekSelect = createDropdown(weeksData, defaultWeek);
weekSelect.classList.add("week-select");
weekCell.appendChild(weekSelect);
row.appendChild(weekCell);
// Render status dropdowns for a selected week
const renderDays = (week) => {
while (row.children.length > 2) {
row.removeChild(row.lastChild);
}
const daysData = historyData[emp.id]?.[week] || {};
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].forEach(day => {
const cell = document.createElement("td");
const selectedStatus = daysData[day] || "Empty";
const daySelect = createDropdown(statusesData, selectedStatus);
daySelect.classList.add("status-select");
daySelect.dataset.day = day;
applyStatusColor(daySelect);
daySelect.addEventListener("change", () => applyStatusColor(daySelect));
cell.appendChild(daySelect);
row.appendChild(cell);
});
};
renderDays(defaultWeek);
// Update days when week changes
weekSelect.addEventListener("change", () => {
renderDays(weekSelect.value);
});
tableBody.appendChild(row);
});
}
async function loadData() {
const namesRes = await fetch("/input/names.json");
const weeksRes = await fetch("/input/selection.json");
const statusRes = await fetch("/input/status.json");
const historyRes = await fetch("/output/history.json");
namesData = await namesRes.json();
weeksData = await weeksRes.json();
statusesData = await statusRes.json();
try {
historyData = await historyRes.json();
} catch {
historyData = {};
}
// Cleanup invalid entries
for (const empId in historyData) {
if (!namesData.some(n => n.id === empId)) {
delete historyData[empId];
continue;
}
for (const week in historyData[empId]) {
if (!weeksData.includes(week)) {
delete historyData[empId][week];
}
}
}
renderTable();
}
document.addEventListener("DOMContentLoaded", loadData);
document.getElementById("saveBtn").addEventListener("click", async () => {
const rows = document.querySelectorAll("#tableBody tr");
rows.forEach(row => {
const empId = row.dataset.empId;
const week = row.querySelector(".week-select").value;
const days = {};
row.querySelectorAll(".status-select").forEach(sel => {
days[sel.dataset.day] = sel.value;
});
if (!historyData[empId]) {
historyData[empId] = {};
}
historyData[empId][week] = days;
});
const response = await fetch("/save-history", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(historyData, null, 2)
});
if (response.ok) {
alert("History saved successfully.");
} else {
alert("Error saving history.");
}
});

عرض الملف

@@ -0,0 +1,93 @@
/* Basic reset for styling */
body {
font-family: Arial, sans-serif;
padding: 10px;
}
.container {
width: 100%;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ccc;
padding: 4px;
text-align: center;
}
/* Right-aligning names in the first column */
td:first-child, th:first-child {
text-align: left;
}
/* Styling the status dropdown */
select {
width: 100%;
padding: 5px;
border-radius: 8px; /* Adding rounded edges to dropdown */
border: 1px solid #ccc;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
/* Status colors for dropdown items */
.status-Empty {
background-color: #f0f0f0;
}
.status-Office {
background-color: #007bff;
color: white;
}
.status-Remote {
background-color: #28a745;
color: white;
}
.status-Casual {
background-color: #ffc107;
color: white;
}
.status-Annual {
background-color: #dc3545;
color: white;
}
.status-Sick {
background-color: #17a2b8;
color: white;
}
.status-Off {
background-color: #6c757d;
color: white;
}
/* Alternating row background colors */
.even-row {
background-color: #f9f9f9;
}
.odd-row {
background-color: white;
}
/* Optional: Hover effect on rows */
tr:hover {
background-color: #e9e9e9;
}