alterzick commited on
Commit
36ec9e8
·
verified ·
1 Parent(s): eb52ab3

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +390 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Balancepos V2
3
- emoji: 🌍
4
- colorFrom: gray
5
- colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: balancepos-v2
3
+ emoji: ⚛️
4
+ colorFrom: green
5
+ colorTo: green
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - QwenSite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,390 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="id">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Balance Position Manager - Hidden Kolom via Checklist</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js"></script>
8
+ <style>
9
+ body { font-family: Arial, sans-serif; margin: 0; background-color: #f4f6f9; }
10
+ h1 { text-align: center; color: #333; padding: 20px; background: #007bff; color: white; margin: 0; }
11
+ .container { max-width: 1600px; margin: 20px auto; padding: 0 20px; display: flex; gap: 20px; }
12
+ .sidebar { width: 300px; background: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); padding: 20px; height: fit-content; }
13
+ .main { flex: 1; }
14
+ .tabs { display: flex; margin-bottom: 20px; background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
15
+ .tab-btn { flex: 1; padding: 15px; text-align: center; background: #e9ecef; cursor: pointer; font-weight: bold; }
16
+ .tab-btn.active { background: #007bff; color: white; }
17
+ .tab-content { display: none; }
18
+ .tab-content.active { display: block; }
19
+ .controls { margin: 20px 0; padding: 15px; background: #f8f9fa; border-radius: 8px; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
20
+ label { font-weight: bold; }
21
+ input, select, button { padding: 10px; font-size: 16px; border-radius: 5px; border: 1px solid #ccc; }
22
+ button { background: #007bff; color: white; border: none; cursor: pointer; }
23
+ button:hover { background: #0056b3; }
24
+ button.warning { background: #ffc107; color: #212529; }
25
+ button.warning:hover { background: #e0a800; }
26
+ button.danger { background: #dc3545; }
27
+ button.danger:hover { background: #c82333; }
28
+ #fileList { list-style: none; padding: 0; margin: 0; }
29
+ #fileList li { padding: 12px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; }
30
+ #fileList li:hover { background: #f8f9fa; }
31
+ .file-actions button { padding: 6px 12px; font-size: 14px; margin-left: 5px; }
32
+ #output { overflow-x: auto; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-radius: 8px; }
33
+ table { border-collapse: collapse; width: 100%; background: white; }
34
+ th, td { border: 1px solid #ddd; padding: 10px; text-align: right; font-size: 13px; }
35
+ th { background: #007bff; color: white; cursor: pointer; position: sticky; top: 0; z-index: 10; }
36
+ th.header, td.header { text-align: left; background: #e9ecef; font-weight: bold; }
37
+ th.sorted-asc::after { content: " ↑"; }
38
+ th.sorted-desc::after { content: " ↓"; }
39
+ tr:nth-child(even) { background: #f8f9fa; }
40
+ .no-data { text-align: center; padding: 30px; color: #666; font-style: italic; }
41
+ .column-list { max-height: 600px; overflow-y: auto; margin-top: 10px; }
42
+ .column-item { display: flex; align-items: center; padding: 8px 0; }
43
+ .column-item input { margin-right: 10px; transform: scale(1.2); }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <h1>Balance Position Manager</h1>
48
+ <div class="container">
49
+ <div class="sidebar" id="sidebar" style="display:none;">
50
+ <h3>Hidden Kolom</h3>
51
+ <button class="warning" onclick="toggleHideMode()" style="width:100%; margin-bottom:15px;">
52
+ <span id="hideModeBtnText">Mode Hidden Kolom</span>
53
+ </button>
54
+ <div id="columnChecklist" class="column-list"></div>
55
+ </div>
56
+
57
+ <div class="main">
58
+ <div class="tabs">
59
+ <div class="tab-btn active" onclick="openTab('upload')">Upload & Proses</div>
60
+ <div class="tab-btn" onclick="openTab('saved')">File Tersimpan (<span id="fileCount">0</span>)</div>
61
+ </div>
62
+
63
+ <div id="upload" class="tab-content active">
64
+ <div class="controls">
65
+ <input type="file" id="fileInput" accept=".txt">
66
+ <input type="text" id="fileNameInput" placeholder="Nama file (misal: Nov 2025)" style="width:300px;">
67
+ <button onclick="processAndSave()">Proses & Simpan</button>
68
+ </div>
69
+
70
+ <div class="controls">
71
+ <label for="typeFilter">Filter Type:</label>
72
+ <select id="typeFilter" onchange="applyFilters()">
73
+ <option value="">Semua Type</option>
74
+ </select>
75
+
76
+ <label for="searchInput" style="margin-left:20px;">Cari Kode:</label>
77
+ <input type="text" id="searchInput" placeholder="Ketik AADI, BBCA, dll" onkeyup="applyFilters()">
78
+ </div>
79
+
80
+ <div id="output"></div>
81
+ </div>
82
+
83
+ <div id="saved" class="tab-content">
84
+ <h3>File yang Telah Disimpan</h3>
85
+ <ul id="fileList"></ul>
86
+ <p class="no-data" id="noFilesMsg">Belum ada file yang disimpan.</p>
87
+ </div>
88
+ </div>
89
+ </div>
90
+
91
+ <script>
92
+ let allRows = [];
93
+ let currentFileKey = null;
94
+ let uniqueTypes = new Set();
95
+ let hiddenColumns = new Set();
96
+ let fullHeaders = [];
97
+ let hideMode = false;
98
+
99
+ function openTab(tabName) {
100
+ document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
101
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
102
+ document.getElementById(tabName).classList.add('active');
103
+ document.querySelector(`.tab-btn[onclick="openTab('${tabName}')"]`).classList.add('active');
104
+ if (tabName === 'saved') loadSavedFiles();
105
+ }
106
+
107
+ async function processAndSave() {
108
+ const fileInput = document.getElementById('fileInput');
109
+ const nameInput = document.getElementById('fileNameInput').value.trim();
110
+ const file = fileInput.files[0];
111
+
112
+ if (!file) return alert('Pilih file TXT terlebih dahulu!');
113
+ if (!nameInput) return alert('Masukkan nama file!');
114
+
115
+ const content = await readFileAsText(file);
116
+ const parsed = parseContent(content);
117
+
118
+ const key = 'balance_' + Date.now();
119
+ const savedData = {
120
+ name: nameInput,
121
+ date: new Date().toLocaleString('id-ID'),
122
+ content: content,
123
+ headers: parsed.headers,
124
+ rows: parsed.rows,
125
+ types: Array.from(parsed.types)
126
+ };
127
+
128
+ await localforage.setItem(key, savedData);
129
+ alert(`File "${nameInput}" berhasil disimpan!`);
130
+ document.getElementById('fileNameInput').value = '';
131
+ fileInput.value = '';
132
+ displayTable(savedData);
133
+ updateFileCount();
134
+ }
135
+
136
+ function readFileAsText(file) {
137
+ return new Promise((resolve, reject) => {
138
+ const reader = new FileReader();
139
+ reader.onload = e => resolve(e.target.result);
140
+ reader.onerror = reject;
141
+ reader.readAsText(file);
142
+ });
143
+ }
144
+
145
+ function parseContent(content) {
146
+ const lines = content.trim().split('\n');
147
+ if (lines.length < 2) throw new Error('File kosong atau salah format.');
148
+
149
+ const baseHeaders = lines[0].split('|').map(h => h.trim());
150
+
151
+ const localPercentNames = ['Local Isur %', 'Local CP %', 'Local PF %', 'Local IB %', 'Local ID %', 'Local MF %', 'Local SC %', 'Local FD %', 'Local OT %'];
152
+ const foreignPercentNames = ['Foreign IS %', 'Foreign CP %', 'Foreign PF %', 'Foreign IB %', 'Foreign ID %', 'Foreign MF %', 'Foreign SC %', 'Foreign FD %', 'Foreign OT %'];
153
+ const extraHeaders = ['ttotal', ...localPercentNames, 'Total lok %', ...foreignPercentNames];
154
+ const fullHeaders = [...baseHeaders, ...extraHeaders];
155
+
156
+ const rows = [];
157
+ const types = new Set();
158
+
159
+ lines.slice(1).forEach(line => {
160
+ const row = line.split('|').map(c => c.trim());
161
+ if (row.length < baseHeaders.length) return;
162
+
163
+ const code = row[1].toUpperCase();
164
+ const type = row[2];
165
+ types.add(type);
166
+
167
+ const secNum = parseFloat(row[3]) || 0;
168
+ const localValues = row.slice(5, 14).map(v => parseFloat(v) || 0);
169
+ const totalLocal = localValues.reduce((a, b) => a + b, 0);
170
+ const foreignValues = row.slice(15, 24).map(v => parseFloat(v) || 0);
171
+ const totalForeign = foreignValues.reduce((a, b) => a + b, 0);
172
+ const ttotal = totalLocal + totalForeign;
173
+
174
+ const formatPercentValue = (val) => secNum > 0 ? (val / secNum * 100).toFixed(2) : '0.00';
175
+
176
+ const displayRow = [
177
+ ...row,
178
+ ttotal.toLocaleString(),
179
+ ...localValues.map(formatPercentValue),
180
+ formatPercentValue(totalLocal),
181
+ ...foreignValues.map(formatPercentValue)
182
+ ];
183
+
184
+ const numericRow = [
185
+ ...row.slice(0, 3),
186
+ secNum,
187
+ parseFloat(row[4]) || 0,
188
+ ...localValues,
189
+ parseFloat(row[14]) || 0,
190
+ ...foreignValues,
191
+ parseFloat(row[24]) || 0,
192
+ ttotal,
193
+ ...localValues.map(v => secNum > 0 ? v / secNum * 100 : 0),
194
+ secNum > 0 ? totalLocal / secNum * 100 : 0,
195
+ ...foreignValues.map(v => secNum > 0 ? v / secNum * 100 : 0)
196
+ ];
197
+
198
+ rows.push({ display: displayRow, numeric: numericRow, code, type });
199
+ });
200
+
201
+ return { headers: fullHeaders, rows, types };
202
+ }
203
+
204
+ function displayTable(data) {
205
+ allRows = data.rows;
206
+ uniqueTypes = new Set(data.types);
207
+ fullHeaders = data.headers;
208
+ hiddenColumns.clear();
209
+
210
+ let table = '<table id="dataTable"><thead><tr>';
211
+ fullHeaders.forEach((h, idx) => {
212
+ const isText = ['Date', 'Code', 'Type'].includes(h);
213
+ table += `<th class="${isText ? 'header' : ''}" onclick="sortTable(${idx})">${h}</th>`;
214
+ });
215
+ table += '</tr></thead><tbody id="tableBody"></tbody></table>';
216
+
217
+ document.getElementById('output').innerHTML = table;
218
+
219
+ // Isi filter type
220
+ const typeFilter = document.getElementById('typeFilter');
221
+ typeFilter.innerHTML = '<option value="">Semua Type</option>';
222
+ [...uniqueTypes].sort().forEach(t => typeFilter.innerHTML += `<option value="${t}">${t}</option>`);
223
+
224
+ // Tampilkan sidebar
225
+ document.getElementById('sidebar').style.display = 'block';
226
+
227
+ buildColumnChecklist();
228
+ applyFilters();
229
+ }
230
+
231
+ function buildColumnChecklist() {
232
+ const list = document.getElementById('columnChecklist');
233
+ list.innerHTML = '';
234
+
235
+ fullHeaders.forEach((header, idx) => {
236
+ const isMain = ['Date', 'Code', 'Type', 'Sec. Num', 'Price'].includes(header);
237
+ if (isMain) return; // Kolom utama tidak bisa dihidden
238
+
239
+ const div = document.createElement('div');
240
+ div.className = 'column-item';
241
+ div.innerHTML = `
242
+ <input type="checkbox" id="col_${idx}" onchange="toggleColumn(${idx}, this.checked)">
243
+ <label for="col_${idx}">${header}</label>
244
+ `;
245
+ list.appendChild(div);
246
+ });
247
+ }
248
+
249
+ function toggleHideMode() {
250
+ hideMode = !hideMode;
251
+ document.getElementById('hideModeBtnText').textContent = hideMode ? 'Keluar Mode Hidden' : 'Mode Hidden Kolom';
252
+ document.getElementById('columnChecklist').style.display = hideMode ? 'block' : 'none';
253
+ }
254
+
255
+ function toggleColumn(colIndex, checked) {
256
+ if (checked) {
257
+ hiddenColumns.add(colIndex);
258
+ } else {
259
+ hiddenColumns.delete(colIndex);
260
+ }
261
+ applyFilters();
262
+ }
263
+
264
+ function applyFilters() {
265
+ const typeFilter = document.getElementById('typeFilter').value;
266
+ const searchTerm = document.getElementById('searchInput').value.toUpperCase();
267
+
268
+ const tbody = document.getElementById('tableBody');
269
+ tbody.innerHTML = '';
270
+
271
+ const filteredRows = allRows.filter(r => {
272
+ const matchType = !typeFilter || r.type === typeFilter;
273
+ const matchSearch = !searchTerm || r.code.includes(searchTerm);
274
+ return matchType && matchSearch;
275
+ });
276
+
277
+ if (filteredRows.length === 0) {
278
+ tbody.innerHTML = `<tr><td colspan="50" class="no-data">Tidak ada data yang sesuai.</td></tr>`;
279
+ return;
280
+ }
281
+
282
+ filteredRows.forEach(r => {
283
+ const tr = document.createElement('tr');
284
+ r.display.forEach((cell, idx) => {
285
+ if (hiddenColumns.has(idx)) return;
286
+ const td = document.createElement('td');
287
+ td.className = idx <= 2 ? 'header' : '';
288
+ td.textContent = cell;
289
+ tr.appendChild(td);
290
+ });
291
+ tbody.appendChild(tr);
292
+ });
293
+
294
+ // Sembunyikan header yang dihidden
295
+ document.querySelectorAll('#dataTable thead th').forEach((th, idx) => {
296
+ th.style.display = hiddenColumns.has(idx) ? 'none' : '';
297
+ });
298
+ }
299
+
300
+ let sortDirection = {};
301
+ function sortTable(colIndex) {
302
+ if (hiddenColumns.has(colIndex)) return;
303
+
304
+ sortDirection[colIndex] = sortDirection[colIndex] === undefined ? true : !sortDirection[colIndex];
305
+ const asc = sortDirection[colIndex];
306
+
307
+ document.querySelectorAll('#dataTable thead th').forEach(th => th.classList.remove('sorted-asc', 'sorted-desc'));
308
+ document.querySelectorAll('#dataTable thead th')[colIndex].classList.add(asc ? 'sorted-asc' : 'sorted-desc');
309
+
310
+ const sortedRows = [...allRows].sort((a, b) => {
311
+ let va = a.numeric[colIndex];
312
+ let vb = b.numeric[colIndex];
313
+ if (isNaN(va)) {
314
+ va = a.display[colIndex];
315
+ vb = b.display[colIndex];
316
+ return asc ? va.localeCompare(vb) : vb.localeCompare(va);
317
+ }
318
+ return asc ? va - vb : vb - va;
319
+ });
320
+
321
+ allRows = sortedRows;
322
+ applyFilters();
323
+ }
324
+
325
+ async function loadFile(key) {
326
+ const data = await localforage.getItem(key);
327
+ if (data) {
328
+ currentFileKey = key;
329
+ displayTable(data);
330
+ openTab('upload');
331
+ }
332
+ }
333
+
334
+ async function renameFile(key) {
335
+ const data = await localforage.getItem(key);
336
+ const newName = prompt('Masukkan nama baru:', data.name);
337
+ if (newName && newName !== data.name) {
338
+ data.name = newName;
339
+ await localforage.setItem(key, data);
340
+ loadSavedFiles();
341
+ }
342
+ }
343
+
344
+ async function deleteFile(key) {
345
+ if (confirm('Yakin ingin menghapus file ini?')) {
346
+ await localforage.removeItem(key);
347
+ loadSavedFiles();
348
+ updateFileCount();
349
+ }
350
+ }
351
+
352
+ async function loadSavedFiles() {
353
+ const list = document.getElementById('fileList');
354
+ const noMsg = document.getElementById('noFilesMsg');
355
+ list.innerHTML = '';
356
+
357
+ const keys = await localforage.keys();
358
+ const balanceKeys = keys.filter(k => k.startsWith('balance_')).sort((a,b) => b.localeCompare(a));
359
+
360
+ noMsg.style.display = balanceKeys.length === 0 ? 'block' : 'none';
361
+
362
+ for (const key of balanceKeys) {
363
+ const data = await localforage.getItem(key);
364
+ const li = document.createElement('li');
365
+ li.innerHTML = `
366
+ <div>
367
+ <strong>${data.name}</strong><br>
368
+ <small>${data.date}</small>
369
+ </div>
370
+ <div class="file-actions">
371
+ <button onclick="loadFile('${key}')">Lihat</button>
372
+ <button onclick="renameFile('${key}')">Edit Nama</button>
373
+ <button class="danger" onclick="deleteFile('${key}')">Hapus</button>
374
+ </div>
375
+ `;
376
+ list.appendChild(li);
377
+ }
378
+ }
379
+
380
+ function updateFileCount() {
381
+ localforage.keys().then(keys => {
382
+ const count = keys.filter(k => k.startsWith('balance_')).length;
383
+ document.getElementById('fileCount').textContent = count;
384
+ });
385
+ }
386
+
387
+ updateFileCount();
388
+ </script>
389
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=alterzick/balancepos-v2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
390
+ </html>
prompts.txt ADDED
File without changes