теперь логику на rust
Browse files- Cargo.toml +14 -0
- plan.html +83 -35
- server.rs +145 -0
- welder-card.html +73 -23
- welders.html +54 -24
Cargo.toml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```toml
|
| 2 |
+
[package]
|
| 3 |
+
name = "welders-api"
|
| 4 |
+
version = "0.1.0"
|
| 5 |
+
edition = "2021"
|
| 6 |
+
|
| 7 |
+
[dependencies]
|
| 8 |
+
actix-web = "4.0"
|
| 9 |
+
serde = { version = "1.0", features = ["derive"] }
|
| 10 |
+
serde_json = "1.0"
|
| 11 |
+
tokio = { version = "1.0", features = ["full"] }
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
Now let's update the frontend to use this API:
|
plan.html
CHANGED
|
@@ -28,47 +28,16 @@
|
|
| 28 |
</div>
|
| 29 |
</div>
|
| 30 |
</div>
|
| 31 |
-
|
| 32 |
<!-- Plan List -->
|
| 33 |
-
<div class="p-4 space-y-4 mb-24">
|
| 34 |
-
<!-- Plan Item (Completed) -->
|
| 35 |
-
<div class="bg-white rounded-lg shadow overflow-hidden completed-plan">
|
| 36 |
-
<div class="p-4">
|
| 37 |
-
<div class="flex items-center">
|
| 38 |
-
<div class="flex-1">
|
| 39 |
-
<div class="font-medium">HT637</div>
|
| 40 |
-
</div>
|
| 41 |
-
<div class="text-green-500 font-medium">6/6</div>
|
| 42 |
-
</div>
|
| 43 |
-
<div class="mt-2 text-sm text-gray-500">
|
| 44 |
-
<div>Johnson: 2, Smith: 2, Williams: 2</div>
|
| 45 |
-
</div>
|
| 46 |
-
</div>
|
| 47 |
-
</div>
|
| 48 |
-
|
| 49 |
-
<!-- Plan Item (In Progress) -->
|
| 50 |
-
<div class="bg-white rounded-lg shadow overflow-hidden">
|
| 51 |
-
<div class="p-4">
|
| 52 |
-
<div class="flex items-center">
|
| 53 |
-
<div class="flex-1">
|
| 54 |
-
<div class="font-medium">HT52</div>
|
| 55 |
-
</div>
|
| 56 |
-
<div class="text-gray-500">3/∞</div>
|
| 57 |
-
</div>
|
| 58 |
-
<div class="mt-2 text-sm text-gray-500">
|
| 59 |
-
<div>Johnson: 1, Williams: 2</div>
|
| 60 |
-
</div>
|
| 61 |
-
</div>
|
| 62 |
-
</div>
|
| 63 |
-
</div>
|
| 64 |
|
| 65 |
<!-- Fixed Bottom Panel -->
|
| 66 |
<div class="fixed bottom-0 left-0 right-0 bg-white shadow-md z-10 p-4">
|
| 67 |
<div class="font-medium mb-2 text-gray-700">Norms</div>
|
| 68 |
<div class="flex">
|
| 69 |
-
<input type="text" placeholder="Article" class="flex-1 p-2 border rounded-l">
|
| 70 |
-
<input type="
|
| 71 |
-
<button class="bg-gray-600 text-white px-4 rounded-r flex items-center">
|
| 72 |
<i data-feather="plus" class="mr-2"></i>Add
|
| 73 |
</button>
|
| 74 |
</div>
|
|
@@ -76,6 +45,85 @@
|
|
| 76 |
|
| 77 |
<script>
|
| 78 |
feather.replace();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
</script>
|
| 80 |
</body>
|
| 81 |
</html>
|
|
|
|
| 28 |
</div>
|
| 29 |
</div>
|
| 30 |
</div>
|
|
|
|
| 31 |
<!-- Plan List -->
|
| 32 |
+
<div id="plan-list" class="p-4 space-y-4 mb-24"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
<!-- Fixed Bottom Panel -->
|
| 35 |
<div class="fixed bottom-0 left-0 right-0 bg-white shadow-md z-10 p-4">
|
| 36 |
<div class="font-medium mb-2 text-gray-700">Norms</div>
|
| 37 |
<div class="flex">
|
| 38 |
+
<input id="norm-article" type="text" placeholder="Article" class="flex-1 p-2 border rounded-l">
|
| 39 |
+
<input id="norm-time" type="number" placeholder="Time norm" step="0.1" class="w-24 p-2 border-t border-b">
|
| 40 |
+
<button id="add-norm" class="bg-gray-600 text-white px-4 rounded-r flex items-center">
|
| 41 |
<i data-feather="plus" class="mr-2"></i>Add
|
| 42 |
</button>
|
| 43 |
</div>
|
|
|
|
| 45 |
|
| 46 |
<script>
|
| 47 |
feather.replace();
|
| 48 |
+
|
| 49 |
+
// Load plans and norms
|
| 50 |
+
async function loadData() {
|
| 51 |
+
try {
|
| 52 |
+
// Load plans
|
| 53 |
+
const plansResponse = await fetch('http://localhost:8080/plans');
|
| 54 |
+
const plans = await plansResponse.json();
|
| 55 |
+
|
| 56 |
+
const planList = document.getElementById('plan-list');
|
| 57 |
+
planList.innerHTML = '';
|
| 58 |
+
|
| 59 |
+
plans.forEach(plan => {
|
| 60 |
+
const assignments = Object.entries(plan.assignments)
|
| 61 |
+
.map(([welderId, qty]) => `${welderId}: ${qty}`)
|
| 62 |
+
.join(', ');
|
| 63 |
+
|
| 64 |
+
const planItem = document.createElement('div');
|
| 65 |
+
planItem.className = plan.completed_quantity === plan.total_quantity
|
| 66 |
+
? 'bg-white rounded-lg shadow overflow-hidden completed-plan'
|
| 67 |
+
: 'bg-white rounded-lg shadow overflow-hidden';
|
| 68 |
+
|
| 69 |
+
planItem.innerHTML = `
|
| 70 |
+
<div class="p-4">
|
| 71 |
+
<div class="flex items-center">
|
| 72 |
+
<div class="flex-1">
|
| 73 |
+
<div class="font-medium">${plan.article_code}</div>
|
| 74 |
+
</div>
|
| 75 |
+
<div class="${plan.completed_quantity === plan.total_quantity ? 'text-green-500' : 'text-gray-500'} font-medium">
|
| 76 |
+
${plan.completed_quantity}/${plan.total_quantity}
|
| 77 |
+
</div>
|
| 78 |
+
</div>
|
| 79 |
+
<div class="mt-2 text-sm text-gray-500">
|
| 80 |
+
<div>${assignments}</div>
|
| 81 |
+
</div>
|
| 82 |
+
</div>
|
| 83 |
+
`;
|
| 84 |
+
planList.appendChild(planItem);
|
| 85 |
+
});
|
| 86 |
+
|
| 87 |
+
// Load norms (could be used for display if needed)
|
| 88 |
+
const normsResponse = await fetch('http://localhost:8080/norms');
|
| 89 |
+
const norms = await normsResponse.json();
|
| 90 |
+
console.log('Loaded norms:', norms);
|
| 91 |
+
} catch (error) {
|
| 92 |
+
console.error('Error loading data:', error);
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// Add new norm
|
| 97 |
+
document.getElementById('add-norm').addEventListener('click', async () => {
|
| 98 |
+
const article = document.getElementById('norm-article').value.trim();
|
| 99 |
+
const timeNorm = parseFloat(document.getElementById('norm-time').value);
|
| 100 |
+
|
| 101 |
+
if (article && !isNaN(timeNorm) && timeNorm > 0) {
|
| 102 |
+
try {
|
| 103 |
+
const response = await fetch('http://localhost:8080/norms', {
|
| 104 |
+
method: 'POST',
|
| 105 |
+
headers: {
|
| 106 |
+
'Content-Type': 'application/json',
|
| 107 |
+
},
|
| 108 |
+
body: JSON.stringify({
|
| 109 |
+
article_code: article,
|
| 110 |
+
time_norm: timeNorm
|
| 111 |
+
}),
|
| 112 |
+
});
|
| 113 |
+
|
| 114 |
+
if (response.ok) {
|
| 115 |
+
document.getElementById('norm-article').value = '';
|
| 116 |
+
document.getElementById('norm-time').value = '';
|
| 117 |
+
console.log('Norm added successfully');
|
| 118 |
+
}
|
| 119 |
+
} catch (error) {
|
| 120 |
+
console.error('Error adding norm:', error);
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
+
});
|
| 124 |
+
|
| 125 |
+
// Load initial data
|
| 126 |
+
loadData();
|
| 127 |
</script>
|
| 128 |
</body>
|
| 129 |
</html>
|
server.rs
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
```rust
|
| 2 |
+
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
|
| 3 |
+
use serde::{Deserialize, Serialize};
|
| 4 |
+
use std::sync::Mutex;
|
| 5 |
+
use std::collections::HashMap;
|
| 6 |
+
|
| 7 |
+
// Data models
|
| 8 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 9 |
+
struct Welder {
|
| 10 |
+
id: u32,
|
| 11 |
+
name: String,
|
| 12 |
+
articles: Vec<Article>,
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 16 |
+
struct Article {
|
| 17 |
+
id: u32,
|
| 18 |
+
code: String,
|
| 19 |
+
quantity: u32,
|
| 20 |
+
completed: u32,
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
#[derive(Debug, Clone, Serialize, Deserialize)]
|
| 24 |
+
struct Plan {
|
| 25 |
+
id: u32,
|
| 26 |
+
article_code: String,
|
| 27 |
+
total_quantity: u32,
|
| 28 |
+
completed_quantity: u32,
|
| 29 |
+
assignments: HashMap<u32, u32>, // welder_id -> quantity
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
#[derive(Debug, Serialize, Deserialize)]
|
| 33 |
+
struct Norm {
|
| 34 |
+
article_code: String,
|
| 35 |
+
time_norm: f32, // hours per unit
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
// App state
|
| 39 |
+
struct AppState {
|
| 40 |
+
welders: Mutex<Vec<Welder>>,
|
| 41 |
+
plans: Mutex<Vec<Plan>>,
|
| 42 |
+
norms: Mutex<Vec<Norm>>,
|
| 43 |
+
next_welder_id: Mutex<u32>,
|
| 44 |
+
next_article_id: Mutex<u32>,
|
| 45 |
+
next_plan_id: Mutex<u32>,
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
// REST API handlers
|
| 49 |
+
async fn get_welders(data: web::Data<AppState>) -> impl Responder {
|
| 50 |
+
let welders = data.welders.lock().unwrap();
|
| 51 |
+
HttpResponse::Ok().json(welders.clone())
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
async fn add_welder(data: web::Data<AppState>, welder: web::Json<Welder>) -> impl Responder {
|
| 55 |
+
let mut welders = data.welders.lock().unwrap();
|
| 56 |
+
let mut next_id = data.next_welder_id.lock().unwrap();
|
| 57 |
+
let new_welder = Welder {
|
| 58 |
+
id: *next_id,
|
| 59 |
+
name: welder.name.clone(),
|
| 60 |
+
articles: vec![],
|
| 61 |
+
};
|
| 62 |
+
*next_id += 1;
|
| 63 |
+
welders.push(new_welder.clone());
|
| 64 |
+
HttpResponse::Created().json(new_welder)
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
async fn get_plans(data: web::Data<AppState>) -> impl Responder {
|
| 68 |
+
let plans = data.plans.lock().unwrap();
|
| 69 |
+
HttpResponse::Ok().json(plans.clone())
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
async fn add_plan(data: web::Data<AppState>, plan: web::Json<Plan>) -> impl Responder {
|
| 73 |
+
let mut plans = data.plans.lock().unwrap();
|
| 74 |
+
let mut next_id = data.next_plan_id.lock().unwrap();
|
| 75 |
+
let mut new_plan = plan.into_inner();
|
| 76 |
+
new_plan.id = *next_id;
|
| 77 |
+
*next_id += 1;
|
| 78 |
+
plans.push(new_plan.clone());
|
| 79 |
+
HttpResponse::Created().json(new_plan)
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
async fn get_norms(data: web::Data<AppState>) -> impl Responder {
|
| 83 |
+
let norms = data.norms.lock().unwrap();
|
| 84 |
+
HttpResponse::Ok().json(norms.clone())
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
async fn add_norm(data: web::Data<AppState>, norm: web::Json<Norm>) -> impl Responder {
|
| 88 |
+
let mut norms = data.norms.lock().unwrap();
|
| 89 |
+
norms.push(norm.into_inner());
|
| 90 |
+
HttpResponse::Created().json(norms.last().unwrap().clone())
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
async fn assign_article(
|
| 94 |
+
data: web::Data<AppState>,
|
| 95 |
+
path: web::Path<(u32,)>,
|
| 96 |
+
article: web::Json<Article>,
|
| 97 |
+
) -> impl Responder {
|
| 98 |
+
let (welder_id,) = path.into_inner();
|
| 99 |
+
let mut welders = data.welders.lock().unwrap();
|
| 100 |
+
let mut next_article_id = data.next_article_id.lock().unwrap();
|
| 101 |
+
|
| 102 |
+
if let Some(welder) = welders.iter_mut().find(|w| w.id == welder_id) {
|
| 103 |
+
let new_article = Article {
|
| 104 |
+
id: *next_article_id,
|
| 105 |
+
code: article.code.clone(),
|
| 106 |
+
quantity: article.quantity,
|
| 107 |
+
completed: 0,
|
| 108 |
+
};
|
| 109 |
+
*next_article_id += 1;
|
| 110 |
+
welder.articles.push(new_article.clone());
|
| 111 |
+
HttpResponse::Created().json(new_article)
|
| 112 |
+
} else {
|
| 113 |
+
HttpResponse::NotFound().json("Welder not found")
|
| 114 |
+
}
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
#[actix_web::main]
|
| 118 |
+
async fn main() -> std::io::Result<()> {
|
| 119 |
+
// Initialize app state
|
| 120 |
+
let app_state = web::Data::new(AppState {
|
| 121 |
+
welders: Mutex::new(vec![]),
|
| 122 |
+
plans: Mutex::new(vec![]),
|
| 123 |
+
norms: Mutex::new(vec![]),
|
| 124 |
+
next_welder_id: Mutex::new(1),
|
| 125 |
+
next_article_id: Mutex::new(1),
|
| 126 |
+
next_plan_id: Mutex::new(1),
|
| 127 |
+
});
|
| 128 |
+
|
| 129 |
+
// Start HTTP server
|
| 130 |
+
HttpServer::new(move || {
|
| 131 |
+
App::new()
|
| 132 |
+
.app_data(app_state.clone())
|
| 133 |
+
.route("/welders", web::get().to(get_welders))
|
| 134 |
+
.route("/welders", web::post().to(add_welder))
|
| 135 |
+
.route("/welders/{id}/articles", web::post().to(assign_article))
|
| 136 |
+
.route("/plans", web::get().to(get_plans))
|
| 137 |
+
.route("/plans", web::post().to(add_plan))
|
| 138 |
+
.route("/norms", web::get().to(get_norms))
|
| 139 |
+
.route("/norms", web::post().to(add_norm))
|
| 140 |
+
})
|
| 141 |
+
.bind("127.0.0.1:8080")?
|
| 142 |
+
.run()
|
| 143 |
+
.await
|
| 144 |
+
}
|
| 145 |
+
```
|
welder-card.html
CHANGED
|
@@ -31,33 +31,83 @@
|
|
| 31 |
</button>
|
| 32 |
</div>
|
| 33 |
</div>
|
| 34 |
-
|
| 35 |
<!-- Articles List -->
|
| 36 |
-
<div class="p-4 space-y-4 mb-20">
|
| 37 |
-
<!-- Month Divider -->
|
| 38 |
-
<div class="bg-gray-200 px-4 py-2 rounded font-medium">August 2023</div>
|
| 39 |
-
|
| 40 |
-
<!-- Article Items -->
|
| 41 |
-
<div class="bg-white rounded-lg shadow overflow-hidden">
|
| 42 |
-
<div class="p-4 border-b flex items-center">
|
| 43 |
-
<div class="flex-1">
|
| 44 |
-
<div class="font-medium">HT637</div>
|
| 45 |
-
<div class="text-sm text-gray-500">4 pieces</div>
|
| 46 |
-
</div>
|
| 47 |
-
<div class="text-green-500 font-medium">4/6</div>
|
| 48 |
-
</div>
|
| 49 |
-
<div class="p-4 flex items-center">
|
| 50 |
-
<div class="flex-1">
|
| 51 |
-
<div class="font-medium">HT52</div>
|
| 52 |
-
<div class="text-sm text-gray-500">3 pieces</div>
|
| 53 |
-
</div>
|
| 54 |
-
<div class="text-gray-500">3/∞</div>
|
| 55 |
-
</div>
|
| 56 |
-
</div>
|
| 57 |
-
</div>
|
| 58 |
|
| 59 |
<script>
|
| 60 |
feather.replace();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
</script>
|
| 62 |
</body>
|
| 63 |
</html>
|
|
|
|
| 31 |
</button>
|
| 32 |
</div>
|
| 33 |
</div>
|
|
|
|
| 34 |
<!-- Articles List -->
|
| 35 |
+
<div id="articles-list" class="p-4 space-y-4 mb-20"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
<script>
|
| 38 |
feather.replace();
|
| 39 |
+
|
| 40 |
+
// Get welder ID from URL
|
| 41 |
+
const urlParams = new URLSearchParams(window.location.search);
|
| 42 |
+
const welderId = urlParams.get('id');
|
| 43 |
+
|
| 44 |
+
// Load welder's articles
|
| 45 |
+
async function loadArticles() {
|
| 46 |
+
try {
|
| 47 |
+
const response = await fetch(`http://localhost:8080/welders/${welderId}/articles`);
|
| 48 |
+
const articles = await response.json();
|
| 49 |
+
|
| 50 |
+
const articlesList = document.getElementById('articles-list');
|
| 51 |
+
articlesList.innerHTML = '';
|
| 52 |
+
|
| 53 |
+
// Group articles by month (simplified example)
|
| 54 |
+
const monthDivider = document.createElement('div');
|
| 55 |
+
monthDivider.className = 'bg-gray-200 px-4 py-2 rounded font-medium';
|
| 56 |
+
monthDivider.textContent = new Date().toLocaleString('default', { month: 'long', year: 'numeric' });
|
| 57 |
+
articlesList.appendChild(monthDivider);
|
| 58 |
+
|
| 59 |
+
articles.forEach(article => {
|
| 60 |
+
const articleItem = document.createElement('div');
|
| 61 |
+
articleItem.className = 'bg-white rounded-lg shadow overflow-hidden';
|
| 62 |
+
articleItem.innerHTML = `
|
| 63 |
+
<div class="p-4 border-b flex items-center">
|
| 64 |
+
<div class="flex-1">
|
| 65 |
+
<div class="font-medium">${article.code}</div>
|
| 66 |
+
<div class="text-sm text-gray-500">${article.quantity} pieces</div>
|
| 67 |
+
</div>
|
| 68 |
+
<div class="${article.completed === article.quantity ? 'text-green-500' : 'text-gray-500'} font-medium">
|
| 69 |
+
${article.completed}/${article.quantity}
|
| 70 |
+
</div>
|
| 71 |
+
</div>
|
| 72 |
+
`;
|
| 73 |
+
articlesList.appendChild(articleItem);
|
| 74 |
+
});
|
| 75 |
+
} catch (error) {
|
| 76 |
+
console.error('Error loading articles:', error);
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
// Add new article
|
| 81 |
+
document.querySelector('button.bg-blue-500').addEventListener('click', async () => {
|
| 82 |
+
const codeInput = document.querySelector('input[placeholder="Article"]');
|
| 83 |
+
const qtyInput = document.querySelector('input[placeholder="Qty"]');
|
| 84 |
+
|
| 85 |
+
const code = codeInput.value.trim();
|
| 86 |
+
const quantity = parseInt(qtyInput.value);
|
| 87 |
+
|
| 88 |
+
if (code && quantity > 0) {
|
| 89 |
+
try {
|
| 90 |
+
const response = await fetch(`http://localhost:8080/welders/${welderId}/articles`, {
|
| 91 |
+
method: 'POST',
|
| 92 |
+
headers: {
|
| 93 |
+
'Content-Type': 'application/json',
|
| 94 |
+
},
|
| 95 |
+
body: JSON.stringify({ code, quantity }),
|
| 96 |
+
});
|
| 97 |
+
|
| 98 |
+
if (response.ok) {
|
| 99 |
+
codeInput.value = '';
|
| 100 |
+
qtyInput.value = '';
|
| 101 |
+
await loadArticles();
|
| 102 |
+
}
|
| 103 |
+
} catch (error) {
|
| 104 |
+
console.error('Error adding article:', error);
|
| 105 |
+
}
|
| 106 |
+
}
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
+
// Load initial data
|
| 110 |
+
loadArticles();
|
| 111 |
</script>
|
| 112 |
</body>
|
| 113 |
</html>
|
welders.html
CHANGED
|
@@ -92,33 +92,63 @@
|
|
| 92 |
<script>
|
| 93 |
// Initialize feather icons
|
| 94 |
feather.replace();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
-
//
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
|
|
|
| 119 |
});
|
| 120 |
|
| 121 |
-
//
|
|
|
|
|
|
|
| 122 |
document.getElementById('plan-btn').addEventListener('click', () => {
|
| 123 |
window.location.href = 'plan.html';
|
| 124 |
});
|
|
|
|
| 92 |
<script>
|
| 93 |
// Initialize feather icons
|
| 94 |
feather.replace();
|
| 95 |
+
// Fetch welders from API
|
| 96 |
+
async function loadWelders() {
|
| 97 |
+
try {
|
| 98 |
+
const response = await fetch('http://localhost:8080/welders');
|
| 99 |
+
const welders = await response.json();
|
| 100 |
+
|
| 101 |
+
const weldersList = document.getElementById('welders-list');
|
| 102 |
+
welders.forEach(welder => {
|
| 103 |
+
const welderItem = document.createElement('div');
|
| 104 |
+
welderItem.className = 'panel p-4 rounded-lg cursor-pointer transition hover:bg-gray-50';
|
| 105 |
+
welderItem.innerHTML = `
|
| 106 |
+
<div class="flex items-center">
|
| 107 |
+
<i data-feather="user" class="text-gray-500 mr-3"></i>
|
| 108 |
+
<span class="font-medium">${welder.name}</span>
|
| 109 |
+
<i data-feather="chevron-right" class="ml-auto text-gray-400"></i>
|
| 110 |
+
</div>
|
| 111 |
+
`;
|
| 112 |
+
welderItem.addEventListener('click', () => {
|
| 113 |
+
window.location.href = `welder-card.html?id=${welder.id}`;
|
| 114 |
+
});
|
| 115 |
+
weldersList.appendChild(welderItem);
|
| 116 |
+
});
|
| 117 |
+
feather.replace();
|
| 118 |
+
} catch (error) {
|
| 119 |
+
console.error('Error loading welders:', error);
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
|
| 123 |
+
// Add new welder
|
| 124 |
+
document.querySelector('button.btn-primary').addEventListener('click', async () => {
|
| 125 |
+
const input = document.querySelector('input');
|
| 126 |
+
const name = input.value.trim();
|
| 127 |
+
|
| 128 |
+
if (name) {
|
| 129 |
+
try {
|
| 130 |
+
const response = await fetch('http://localhost:8080/welders', {
|
| 131 |
+
method: 'POST',
|
| 132 |
+
headers: {
|
| 133 |
+
'Content-Type': 'application/json',
|
| 134 |
+
},
|
| 135 |
+
body: JSON.stringify({ name }),
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
if (response.ok) {
|
| 139 |
+
input.value = '';
|
| 140 |
+
document.getElementById('welders-list').innerHTML = '';
|
| 141 |
+
await loadWelders();
|
| 142 |
+
}
|
| 143 |
+
} catch (error) {
|
| 144 |
+
console.error('Error adding welder:', error);
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
});
|
| 148 |
|
| 149 |
+
// Load initial data
|
| 150 |
+
loadWelders();
|
| 151 |
+
// Plan button click handler
|
| 152 |
document.getElementById('plan-btn').addEventListener('click', () => {
|
| 153 |
window.location.href = 'plan.html';
|
| 154 |
});
|