1use ravenrs::prelude::*;
4use ravenrs_spreadsheet::{Spreadsheet, SpreadsheetState};
5use serde_json::json;
6
7const LINES: [(&str, f64, f64); 8] = [
9 ("Moradia", 2400.0, 2400.0),
10 ("Mercado", 1300.0, 1452.37),
11 ("Transporte", 600.0, 488.9),
12 ("Saúde", 450.0, 512.0),
13 ("Educação", 900.0, 900.0),
14 ("Lazer", 400.0, 615.5),
15 ("Contas da casa", 520.0, 498.13),
16 ("Reserva", 800.0, 800.0),
17];
18
19pub fn sample() -> SpreadsheetState {
21 let sheet = SpreadsheetState::new();
22 sheet.set_file_name("Orçamento");
23 let set = |cell: &str, input: &str| {
24 let _ = sheet.set_cell(cell, input);
25 };
26 let run = |command: &str, params: serde_json::Value| {
27 let _ = sheet.execute(command, params);
28 };
29 run("sheet.rename", json!({ "name": "Orçamento" }));
30 set("A1", "Orçamento de novembro");
31 run("home.bold", json!({ "range": "A1", "on": true }));
32 run("home.fontSize", json!({ "range": "A1", "size": 16 }));
33 for (cell, title) in
34 [("A3", "Categoria"), ("B3", "Previsto"), ("C3", "Gasto"), ("D3", "Diferença"), ("E3", "% usado")]
35 {
36 set(cell, title);
37 }
38 run("home.bold", json!({ "range": "A3:E3", "on": true }));
39 run("home.fillColor", json!({ "range": "A3:E3", "color": "#DCE6F1" }));
40 run("home.borders", json!({ "range": "A3:E3", "border": "bottom" }));
41 for (i, (name, budget, spent)) in LINES.iter().enumerate() {
42 let row = i + 4;
43 set(&format!("A{row}"), name);
44 set(&format!("B{row}"), &budget.to_string());
45 set(&format!("C{row}"), &spent.to_string());
46 set(&format!("D{row}"), &format!("=B{row}-C{row}"));
47 set(&format!("E{row}"), &format!("=C{row}/B{row}"));
48 }
49 let last = LINES.len() + 3;
50 let total = last + 1;
51 set(&format!("A{total}"), "Total");
52 for col in ["B", "C", "D"] {
53 set(&format!("{col}{total}"), &format!("=SUM({col}4:{col}{last})"));
54 }
55 set(&format!("E{total}"), &format!("=C{total}/B{total}"));
56 run("home.bold", json!({ "range": format!("A{total}:E{total}"), "on": true }));
57 run("home.borders", json!({ "range": format!("A{total}:E{total}"), "border": "top" }));
58 run(
59 "home.numberFormat",
60 json!({ "range": format!("B4:D{total}"), "code": "\"R$\" #,##0.00;[Red]-\"R$\" #,##0.00" }),
61 );
62 run("home.numberFormat", json!({ "range": format!("E4:E{total}"), "code": "0%" }));
63 run("home.columnWidth", json!({ "cols": "A:A", "width": 150 }));
64 run("home.columnWidth", json!({ "cols": "B:D", "width": 110 }));
65
66 run("home.insertSheet", json!({ "name": "Resumo", "before": 9 }));
67 run("sheet.activate", json!({ "sheet": 1 }));
68 set("A1", "Maior gasto");
69 set(
70 "B1",
71 &format!("=INDEX(Orçamento!A4:A{last},MATCH(MAX(Orçamento!C4:C{last}),Orçamento!C4:C{last},0))"),
72 );
73 set("A2", "Acima do previsto");
74 set("B2", &format!("=COUNTIF(Orçamento!D4:D{last},\"<0\")"));
75 set("A3", "Sobra do mês");
76 set("B3", &format!("=Orçamento!D{total}"));
77 run("home.numberFormat", json!({ "range": "B3", "code": "\"R$\" #,##0.00" }));
78 run("home.columnWidth", json!({ "cols": "A:B", "width": 140 }));
79 run("sheet.activate", json!({ "sheet": 0 }));
80 let _ = sheet.select("B4");
81 sheet.with_session(|s| {
82 if let Some(doc) = s.active_mut() {
83 doc.undo.clear();
84 }
85 });
86 sheet
87}
88
89pub fn app() -> impl View {
91 Spreadsheet::new(&sample())
92}
93
94#[cfg(target_arch = "wasm32")]
95mod web_entry {
96 use std::cell::RefCell;
97
98 use wasm_bindgen::prelude::*;
99
100 thread_local! {
101 static APP: RefCell<Option<ravenrs::web::WebApp>> = const { RefCell::new(None) };
102 }
103
104 #[wasm_bindgen(start)]
106 pub fn start() {
107 console_error_panic_hook::set_once();
108 wasm_bindgen_futures::spawn_local(async {
109 match ravenrs::web::start(super::app(), ravenrs::web::WebOptions::new("raven-canvas")).await {
110 Ok(app) => APP.with(|a| *a.borrow_mut() = Some(app)),
111 Err(e) => web_sys::console::error_1(&e.to_string().into()),
112 }
113 });
114 }
115
116 #[wasm_bindgen]
118 pub fn stop() {
119 APP.with(|a| a.borrow_mut().take());
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 #![allow(clippy::unwrap_used)]
126
127 use super::*;
128
129 #[test]
130 fn the_sample_adds_up() {
131 let sheet = sample();
132 assert_eq!(sheet.sheet_names(), ["Orçamento", "Resumo"]);
133 assert_eq!(sheet.cell_text("B12"), "R$ 7,370.00");
134 assert_eq!(sheet.cell_text("E5"), "112%");
135 assert!(
136 sheet.cell_text("D5").starts_with('-'),
137 "overspent shows negative: {}",
138 sheet.cell_text("D5")
139 );
140 sheet.execute("sheet.activate", json!({ "sheet": 1 })).unwrap();
141 assert_eq!(sheet.cell_text("B1"), "Moradia");
142 assert_eq!(sheet.cell_text("B2"), "3");
143 assert!(!sheet.can_undo());
144 }
145}