alterzick commited on
Commit
c080690
·
verified ·
1 Parent(s): efc0f1e

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +654 -19
  3. prompts.txt +2 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Ksei
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: ksei
3
+ emoji: ⚛️
4
+ colorFrom: pink
5
+ colorTo: gray
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,654 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>Stock Ownership Analytics Tool</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.3.0/papaparse.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
+ <link href="https://cdn.jsdelivr.net/npm/daisyui@3.9.4/dist/full.css" rel="stylesheet" type="text/css" />
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css"/>
12
+ <style>
13
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
14
+ body {
15
+ font-family: 'Inter', sans-serif;
16
+ }
17
+ .chart-container {
18
+ position: relative;
19
+ height: 350px;
20
+ width: 100%;
21
+ }
22
+ .loading-spinner {
23
+ border-top-color: #3498db;
24
+ animation: spinner 1.2s linear infinite;
25
+ }
26
+ @keyframes spinner {
27
+ 0% { transform: rotate(0deg); }
28
+ 100% { transform: rotate(360deg); }
29
+ }
30
+ .database-item {
31
+ transition: all 0.2s;
32
+ }
33
+ .database-item:hover {
34
+ transform: translateY(-2px);
35
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
36
+ }
37
+ </style>
38
+ </head>
39
+ <body class="bg-gradient-to-br from-slate-50 to-slate-100 min-h-screen">
40
+
41
+ <!-- Navbar -->
42
+ <nav class="bg-white shadow-lg sticky top-0 z-50 border-b">
43
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
44
+ <div class="flex justify-between items-center h-16">
45
+ <div class="flex items-center">
46
+ <div class="text-2xl font-bold text-indigo-600 flex items-center gap-2">
47
+ <i class="fas fa-chart-line"></i>
48
+ <span>StockOwnership Insights</span>
49
+ </div>
50
+ </div>
51
+ <div class="hidden md:flex space-x-8">
52
+ <a href="#dashboard" class="text-gray-700 hover:text-indigo-600 font-medium">Dashboard</a>
53
+ <a href="#uploader" class="text-gray-700 hover:text-indigo-600 font-medium">Upload Data</a>
54
+ <a href="#database" class="text-gray-700 hover:text-indigo-600 font-medium">Database</a>
55
+ <a href="#download" class="text-gray-700 hover:text-indigo-600 font-medium">Export</a>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </nav>
60
+
61
+ <!-- Hero Section -->
62
+ <section class="py-16 px-6 text-center bg-gradient-to-r from-indigo-600 to-purple-600 text-white">
63
+ <div class="max-w-4xl mx-auto">
64
+ <h1 class="text-4xl md:text-5xl font-bold mb-6 leading-tight">
65
+ Analisis Kepemilikan Saham
66
+ </h1>
67
+ <p class="text-lg md:text-xl opacity-90 mb-10">
68
+ Pantau pertumbuhan kepemilikan saham oleh investor lokal, asing, dan institusi secara real-time.
69
+ Ekstrak data dari KSEI dan analisis dalam dashboard interaktif.
70
+ </p>
71
+ <button onclick="scrollToSection('uploader')" class="bg-white text-indigo-700 font-semibold px-8 py-3 rounded-full shadow-lg hover:shadow-xl transform hover:-translate-y-1 transition-all duration-200 flex items-center gap-2 mx-auto">
72
+ <i class="fas fa-file-import"></i> Mulai Analisis
73
+ </button>
74
+ </div>
75
+ </section>
76
+
77
+ <!-- Upload Section -->
78
+ <section id="uploader" class="py-16 px-6 bg-white">
79
+ <div class="max-w-4xl mx-auto">
80
+ <div class="text-center mb-10">
81
+ <h2 class="text-3xl font-bold text-gray-800 mb-4">Unggah Data KSEI</h2>
82
+ <p class="text-gray-600">
83
+ Unggah file CSV yang diunduh dari <a href="https://web.ksei.co.id/Download/" target="_blank" class="text-indigo-600 hover:underline">KSEI</a>.
84
+ Format yang didukung: CSV dengan pembatas <strong>|</strong> dan header <strong>Ticker|Nama Saham|Lokal|Asing|Institusi|Tanggal</strong>.
85
+ </p>
86
+ </div>
87
+
88
+ <div id="upload-box" class="border-2 border-dashed border-gray-300 rounded-xl p-10 text-center hover:border-indigo-400 transition-colors duration-200 cursor-pointer relative overflow-hidden group">
89
+ <input type="file" id="csv-upload" accept=".csv" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" onchange="handleFileSelect(event)" />
90
+ <div class="flex flex-col items-center">
91
+ <i class="fas fa-cloud-upload-alt text-5xl text-gray-400 group-hover:text-indigo-500 transition-colors duration-200"></i>
92
+ <p class="mt-4 text-gray-600 font-medium">Seret dan lepas file CSV, atau klik untuk memilih</p>
93
+ <p class="text-sm text-gray-500 mt-2">Format: CSV dengan delimiter |, maks 10MB</p>
94
+ </div>
95
+ </div>
96
+
97
+ <div id="upload-meta" class="mt-6 bg-gray-50 p-6 rounded-xl hidden">
98
+ <h3 class="text-lg font-semibold text-gray-800 mb-4">Informasi Data</h3>
99
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
100
+ <div>
101
+ <label class="block text-sm font-medium text-gray-700 mb-2">Bulan</label>
102
+ <select id="upload-month" class="w-full border border-gray-300 rounded-lg px-3 py-2">
103
+ <option value="1">Januari</option>
104
+ <option value="2">Februari</option>
105
+ <option value="3">Maret</option>
106
+ <option value="4">April</option>
107
+ <option value="5">Mei</option>
108
+ <option value="6">Juni</option>
109
+ <option value="7">Juli</option>
110
+ <option value="8">Agustus</option>
111
+ <option value="9">September</option>
112
+ <option value="10">Oktober</option>
113
+ <option value="11">November</option>
114
+ <option value="12">Desember</option>
115
+ </select>
116
+ </div>
117
+ <div>
118
+ <label class="block text-sm font-medium text-gray-700 mb-2">Tahun</label>
119
+ <input type="number" id="upload-year" class="w-full border border-gray-300 rounded-lg px-3 py-2"
120
+ value="2023" min="2010" max="2030" />
121
+ </div>
122
+ <div>
123
+ <label class="block text-sm font-medium text-gray-700 mb-2">Nomor Laporan</label>
124
+ <input type="number" id="upload-number" class="w-full border border-gray-300 rounded-lg px-3 py-2"
125
+ value="1" min="1" />
126
+ </div>
127
+ </div>
128
+ <div class="mt-4 flex justify-end">
129
+ <button onclick="saveToDatabase()" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition">
130
+ Simpan ke Database
131
+ </button>
132
+ </div>
133
+ </div>
134
+
135
+ <div id="loading" class="flex flex-col items-center justify-center mt-8 hidden">
136
+ <div class="w-16 h-16 border-4 border-gray-200 border-t-indigo-500 rounded-full loading-spinner"></div>
137
+ <p class="mt-4 text-gray-600">Memproses data...</p>
138
+ </div>
139
+
140
+ <div id="preview" class="mt-8 hidden">
141
+ <h3 class="text-xl font-semibold text-gray-800 mb-4">Pratinjau Data</h3>
142
+ <div class="overflow-x-auto bg-gray-50 rounded-lg border">
143
+ <table id="data-preview-table" class="min-w-full text-sm"></table>
144
+ </div>
145
+ <div class="mt-4 flex justify-end">
146
+ <button onclick="processData()" class="bg-indigo-600 text-white px-6 py-2 rounded-lg hover:bg-indigo-700 transition">
147
+ Proses Data
148
+ </button>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ </section>
153
+
154
+ <!-- Database Section -->
155
+ <section id="database" class="py-16 px-6 bg-gray-50">
156
+ <div class="max-w-6xl mx-auto">
157
+ <div class="text-center mb-10">
158
+ <h2 class="text-3xl font-bold text-gray-800 mb-4">Database Laporan Kepemilikan</h2>
159
+ <p class="text-gray-600">Kelola dan akses laporan bulanan kepemilikan saham</p>
160
+ </div>
161
+
162
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-10" id="database-container">
163
+ <!-- Database items will be inserted here -->
164
+ <div class="text-center py-10 text-gray-500">
165
+ <i class="fas fa-database text-5xl mb-4 opacity-30"></i>
166
+ <p>Belum ada data dalam database</p>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </section>
171
+
172
+ <!-- Dashboard Section -->
173
+ <section id="dashboard" class="py-16 px-6 bg-gray-50">
174
+ <div class="max-w-7xl mx-auto">
175
+ <div class="text-center mb-10">
176
+ <h2 class="text-3xl font-bold text-gray-800 mb-4">Dashboard Analitik</h2>
177
+ <p class="text-gray-600">Analisa perubahan kepemilikan saham secara mendalam</p>
178
+ </div>
179
+
180
+ <!-- Summary Cards -->
181
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-10">
182
+ <div class="bg-white p-6 rounded-xl shadow-md border">
183
+ <div class="flex items-center">
184
+ <div class="p-3 rounded-full bg-blue-100 text-blue-600">
185
+ <i class="fas fa-user-friends"></i>
186
+ </div>
187
+ <div class="ml-4">
188
+ <p class="text-sm font-medium text-gray-600">Total Saham</p>
189
+ <p id="total-stocks" class="text-2xl font-bold text-gray-800">-</p>
190
+ </div>
191
+ </div>
192
+ </div>
193
+ <div class="bg-white p-6 rounded-xl shadow-md border">
194
+ <div class="flex items-center">
195
+ <div class="p-3 rounded-full bg-green-100 text-green-600">
196
+ <i class="fas fa-arrow-up"></i>
197
+ </div>
198
+ <div class="ml-4">
199
+ <p class="text-sm font-medium text-gray-600">Saham Asing Naik</p>
200
+ <p id="foreign-up" class="text-2xl font-bold text-gray-800">-</p>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ <div class="bg-white p-6 rounded-xl shadow-md border">
205
+ <div class="flex items-center">
206
+ <div class="p-3 rounded-full bg-purple-100 text-purple-600">
207
+ <i class="fas fa-building"></i>
208
+ </div>
209
+ <div class="ml-4">
210
+ <p class="text-sm font-medium text-gray-600">Saham Institusi</p>
211
+ <p id="institutional-total" class="text-2xl font-bold text-gray-800">-</p>
212
+ </div>
213
+ </div>
214
+ </div>
215
+ <div class="bg-white p-6 rounded-xl shadow-md border">
216
+ <div class="flex items-center">
217
+ <div class="p-3 rounded-full bg-indigo-100 text-indigo-600">
218
+ <i class="fas fa-trending-up"></i>
219
+ </div>
220
+ <div class="ml-4">
221
+ <p class="text-sm font-medium text-gray-600">Rata-Rata Kenaikan Asing</p>
222
+ <p id="foreign-growth" class="text-2xl font-bold text-gray-800">-</p>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ </div>
227
+
228
+ <!-- Charts -->
229
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-10">
230
+ <!-- Ownership Distribution -->
231
+ <div class="bg-white p-6 rounded-xl shadow-md border">
232
+ <h3 class="text-lg font-semibold text-gray-800 mb-4">Distribusi Kepemilikan (Rata-rata)</h3>
233
+ <div class="chart-container">
234
+ <canvas id="ownershipChart"></canvas>
235
+ </div>
236
+ </div>
237
+
238
+ <!-- Growth Trend -->
239
+ <div class="bg-white p-6 rounded-xl shadow-md border">
240
+ <h3 class="text-lg font-semibold text-gray-800 mb-4">Trend Pertumbuhan Asing</h3>
241
+ <div class="chart-container">
242
+ <canvas id="growthChart"></canvas>
243
+ </div>
244
+ </div>
245
+ </div>
246
+
247
+ <!-- Top Movers -->
248
+ <div class="bg-white p-6 rounded-xl shadow-md border mb-10">
249
+ <h3 class="text-lg font-semibold text-gray-800 mb-4">Saham dengan Kenaikan Kepemilikan Asing Tertinggi</h3>
250
+ <div class="overflow-x-auto">
251
+ <table id="top-movers-table" class="min-w-full text-sm">
252
+ <thead>
253
+ <tr class="border-b">
254
+ <th class="py-2 text-left">Ticker</th>
255
+ <th class="py-2 text-left">Nama</th>
256
+ <th class="py-2 text-right">Kenaikan (%)</th>
257
+ </tr>
258
+ </thead>
259
+ <tbody></tbody>
260
+ </table>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </section>
265
+
266
+ <!-- Export Section -->
267
+ <section id="download" class="py-16 px-6 bg-white">
268
+ <div class="max-w-4xl mx-auto text-center">
269
+ <h2 class="text-3xl font-bold text-gray-800 mb-4">Ekspor Hasil Analisis</h2>
270
+ <p class="text-gray-600 mb-8">
271
+ Ekspor data dan hasil analisis ke format CSV untuk disimpan atau digunakan di aplikasi lain.
272
+ </p>
273
+ <button onclick="exportAnalysis()" class="bg-green-600 hover:bg-green-700 text-white font-semibold px-8 py-3 rounded-full shadow-lg flex items-center gap-2 mx-auto transition">
274
+ <i class="fas fa-file-export"></i> Ekspor ke CSV
275
+ </button>
276
+ </div>
277
+ </section>
278
+
279
+ <!-- Footer -->
280
+ <footer class="py-8 bg-gray-800 text-white text-center">
281
+ <div class="max-w-4xl mx-auto px-6">
282
+ <p>StockOwnership Insights Tool &copy; 2023 | Data bersumber dari <a href="https://web.ksei.co.id" class="text-indigo-400 hover:underline">KSEI</a></p>
283
+ <p class="text-sm text-gray-400 mt-2">Alat analisis ini tidak berafiliasi dengan KSEI. Gunakan dengan tanggung jawab Anda sendiri.</p>
284
+ </div>
285
+ </footer>
286
+
287
+ <script>
288
+ // Global Variables
289
+ let rawData = [];
290
+ let processedData = [];
291
+ let ownershipChart = null;
292
+ let growthChart = null;
293
+ let database = JSON.parse(localStorage.getItem('ownershipDatabase')) || [];
294
+
295
+ // Load database on startup
296
+ document.addEventListener('DOMContentLoaded', function() {
297
+ renderDatabase();
298
+ });
299
+
300
+ // Scroll to section
301
+ function scrollToSection(id) {
302
+ document.getElementById(id).scrollIntoView({ behavior: 'smooth' });
303
+ }
304
+
305
+ // Handle file upload
306
+ function handleFileSelect(event) {
307
+ const file = event.target.files[0];
308
+ if (!file) return;
309
+
310
+ document.getElementById('loading').classList.remove('hidden');
311
+ document.getElementById('preview').classList.add('hidden');
312
+ document.getElementById('upload-meta').classList.add('hidden');
313
+
314
+ const reader = new FileReader();
315
+ reader.onload = function(e) {
316
+ const csv = e.target.result;
317
+ Papa.parse(csv, {
318
+ header: true,
319
+ skipEmptyLines: true,
320
+ delimiter: "|", // Specify pipe as delimiter
321
+ complete: function(results) {
322
+ rawData = results.data;
323
+ previewData(rawData.slice(0, 10));
324
+ document.getElementById('loading').classList.add('hidden');
325
+ document.getElementById('preview').classList.remove('hidden');
326
+ document.getElementById('upload-meta').classList.remove('hidden');
327
+ },
328
+ error: function(error) {
329
+ alert("Error parsing CSV: " + error);
330
+ document.getElementById('loading').classList.add('hidden');
331
+ }
332
+ });
333
+ };
334
+ reader.readAsText(file);
335
+ }
336
+
337
+ // Preview data
338
+ function previewData(data) {
339
+ const table = document.getElementById('data-preview-table');
340
+ table.innerHTML = '';
341
+
342
+ // Create header
343
+ const thead = document.createElement('thead');
344
+ thead.innerHTML = '<tr class="border-b"><th class="py-2 text-left">Ticker</th><th class="py-2 text-left">Nama</th><th class="py-2 text-right">Lokal</th><th class="py-2 text-right">Asing</th><th class="py-2 text-right">Institusi</th><th class="py-2 text-right">Tanggal</th></tr>';
345
+ table.appendChild(thead);
346
+
347
+ // Create body
348
+ const tbody = document.createElement('tbody');
349
+ data.forEach(row => {
350
+ const tr = document.createElement('tr');
351
+ tr.className = 'border-b hover:bg-gray-50';
352
+ tr.innerHTML = `
353
+ <td class="py-2 font-mono">${row.Ticker || '-'}</td>
354
+ <td class="py-2">${row['Nama Saham'] || '-'}</td>
355
+ <td class="py-2 text-right">${formatNumber(row.Lokal)}</td>
356
+ <td class="py-2 text-right">${formatNumber(row.Asing)}</td>
357
+ <td class="py-2 text-right">${formatNumber(row.Institusi)}</td>
358
+ <td class="py-2 text-right">${row.Tanggal || '-'}</td>
359
+ `;
360
+ tbody.appendChild(tr);
361
+ });
362
+ table.appendChild(tbody);
363
+ }
364
+
365
+ // Process data
366
+ function processData() {
367
+ // Convert string numbers to floats
368
+ processedData = rawData.map(row => ({
369
+ ...row,
370
+ Lokal: parseFloat(row.Lokal) || 0,
371
+ Asing: parseFloat(row.Asing) || 0,
372
+ Institusi: parseFloat(row.Institusi) || 0
373
+ })).filter(row => row.Ticker && !isNaN(row.Asing));
374
+
375
+ // Calculate metrics
376
+ updateMetrics();
377
+
378
+ // Render charts
379
+ renderCharts();
380
+
381
+ // Fill top movers
382
+ renderTopMovers();
383
+
384
+ // Show dashboard
385
+ scrollToSection('dashboard');
386
+ }
387
+
388
+ // Format numbers with commas
389
+ function formatNumber(num) {
390
+ if (typeof num !== 'number') num = parseFloat(num);
391
+ if (isNaN(num)) return '-';
392
+ return num.toLocaleString('id-ID', { maximumFractionDigits: 2 });
393
+ }
394
+
395
+ // Update summary metrics
396
+ function updateMetrics() {
397
+ const totalStocks = processedData.length;
398
+ const foreignUp = processedData.filter(row => row.Asing > 0).length;
399
+ const institutionalTotal = processedData.reduce((sum, row) => sum + row.Institusi, 0);
400
+ const avgForeignGrowth = processedData.length > 0
401
+ ? processedData.reduce((sum, row) => sum + row.Asing, 0) / processedData.length
402
+ : 0;
403
+
404
+ document.getElementById('total-stocks').textContent = totalStocks;
405
+ document.getElementById('foreign-up').textContent = foreignUp;
406
+ document.getElementById('institutional-total').textContent = formatNumber(institutionalTotal);
407
+ document.getElementById('foreign-growth').textContent = formatNumber(avgForeignGrowth) + '%';
408
+ }
409
+
410
+ // Render charts
411
+ function renderCharts() {
412
+ // Ownership distribution chart
413
+ const avgLocal = processedData.reduce((sum, row) => sum + row.Lokal, 0) / processedData.length;
414
+ const avgForeign = processedData.reduce((sum, row) => sum + row.Asing, 0) / processedData.length;
415
+ const avgInstitutional = processedData.reduce((sum, row) => sum + row.Institusi, 0) / processedData.length;
416
+
417
+ const ctx1 = document.getElementById('ownershipChart').getContext('2d');
418
+ if (ownershipChart) ownershipChart.destroy();
419
+ ownershipChart = new Chart(ctx1, {
420
+ type: 'pie',
421
+ data: {
422
+ labels: ['Investor Lokal', 'Investor Asing', 'Institusi'],
423
+ datasets: [{
424
+ data: [avgLocal, avgForeign, avgInstitutional],
425
+ backgroundColor: ['#6366F1', '#10B981', '#8B5CF6'],
426
+ borderWidth: 2,
427
+ borderColor: '#FFFFFF',
428
+ hoverOffset: 10
429
+ }]
430
+ },
431
+ options: {
432
+ responsive: true,
433
+ plugins: {
434
+ legend: {
435
+ position: 'bottom'
436
+ }
437
+ }
438
+ }
439
+ });
440
+
441
+ // Growth trend chart
442
+ // Group by date and calculate average foreign ownership
443
+ const groupedByDate = {};
444
+ processedData.forEach(row => {
445
+ const date = row.Tanggal;
446
+ if (!groupedByDate[date]) {
447
+ groupedByDate[date] = { sum: 0, count: 0 };
448
+ }
449
+ groupedByDate[date].sum += parseFloat(row.Asing) || 0;
450
+ groupedByDate[date].count++;
451
+ });
452
+
453
+ const dates = Object.keys(groupedByDate).sort();
454
+ const avgGrowth = dates.map(date => groupedByDate[date].sum / groupedByDate[date].count);
455
+
456
+ const ctx2 = document.getElementById('growthChart').getContext('2d');
457
+ if (growthChart) growthChart.destroy();
458
+ growthChart = new Chart(ctx2, {
459
+ type: 'line',
460
+ data: {
461
+ labels: dates,
462
+ datasets: [{
463
+ label: 'Rata-rata Kepemilikan Asing (%)',
464
+ data: avgGrowth,
465
+ borderColor: '#EC4899',
466
+ backgroundColor: 'rgba(236, 72, 153, 0.1)',
467
+ fill: true,
468
+ tension: 0.4,
469
+ pointBackgroundColor: '#EC4899'
470
+ }]
471
+ },
472
+ options: {
473
+ responsive: true,
474
+ plugins: {
475
+ legend: {
476
+ display: false
477
+ }
478
+ },
479
+ scales: {
480
+ y: {
481
+ beginAtZero: true,
482
+ title: {
483
+ display: true,
484
+ text: 'Pertumbuhan (%)'
485
+ }
486
+ },
487
+ x: {
488
+ title: {
489
+ display: true,
490
+ text: 'Tanggal'
491
+ }
492
+ }
493
+ }
494
+ }
495
+ });
496
+ }
497
+
498
+ // Render top movers
499
+ function renderTopMovers() {
500
+ const sorted = [...processedData]
501
+ .sort((a, b) => (parseFloat(b.Asing) || 0) - (parseFloat(a.Asing) || 0))
502
+ .slice(0, 10);
503
+
504
+ const table = document.getElementById('top-movers-table').getElementsByTagName('tbody')[0];
505
+ table.innerHTML = '';
506
+
507
+ sorted.forEach(row => {
508
+ const tr = document.createElement('tr');
509
+ tr.className = 'border-b hover:bg-gray-50';
510
+ tr.innerHTML = `
511
+ <td class="py-2 font-mono font-semibold">${row.Ticker}</td>
512
+ <td class="py-2">${row['Nama Saham'] || '-'}</td>
513
+ <td class="py-2 text-right text-green-600 font-semibold">${formatNumber(row.Asing)}%</td>
514
+ `;
515
+ table.appendChild(tr);
516
+ });
517
+ }
518
+
519
+ // Save to database
520
+ function saveToDatabase() {
521
+ if (rawData.length === 0) {
522
+ alert('Belum ada data untuk disimpan.');
523
+ return;
524
+ }
525
+
526
+ const month = document.getElementById('upload-month').value;
527
+ const year = document.getElementById('upload-year').value;
528
+ const number = document.getElementById('upload-number').value;
529
+ const monthNames = ["Januari", "Februari", "Maret", "April", "Mei", "Juni",
530
+ "Juli", "Agustus", "September", "Oktober", "November", "Desember"];
531
+
532
+ const entry = {
533
+ id: Date.now(),
534
+ month: parseInt(month),
535
+ year: parseInt(year),
536
+ number: parseInt(number),
537
+ monthName: monthNames[month - 1],
538
+ date: new Date().toISOString(),
539
+ recordCount: rawData.length
540
+ };
541
+
542
+ database.push(entry);
543
+ database.sort((a, b) => new Date(b.year, b.month) - new Date(a.year, a.month));
544
+
545
+ localStorage.setItem('ownershipDatabase', JSON.stringify(database));
546
+ renderDatabase();
547
+
548
+ alert(`Data berhasil disimpan ke database!`);
549
+ }
550
+
551
+ // Render database
552
+ function renderDatabase() {
553
+ const container = document.getElementById('database-container');
554
+ container.innerHTML = '';
555
+
556
+ if (database.length === 0) {
557
+ container.innerHTML = `
558
+ <div class="text-center py-10 text-gray-500 col-span-2">
559
+ <i class="fas fa-database text-5xl mb-4 opacity-30"></i>
560
+ <p>Belum ada data dalam database</p>
561
+ </div>
562
+ `;
563
+ return;
564
+ }
565
+
566
+ database.forEach(item => {
567
+ const monthNames = ["Januari", "Februari", "Maret", "April", "Mei", "Juni",
568
+ "Juli", "Agustus", "September", "Oktober", "November", "Desember"];
569
+
570
+ const itemDiv = document.createElement('div');
571
+ itemDiv.className = 'bg-white p-6 rounded-xl shadow-md border database-item';
572
+ itemDiv.innerHTML = `
573
+ <div class="flex justify-between items-start">
574
+ <div>
575
+ <span class="inline-block bg-indigo-100 text-indigo-800 text-xs px-3 py-1 rounded-full font-medium">
576
+ No. ${item.number}
577
+ </span>
578
+ <h3 class="text-xl font-bold text-gray-800 mt-2">${item.monthName} ${item.year}</h3>
579
+ <p class="text-gray-600">Diunggah: ${new Date(item.date).toLocaleDateString('id-ID')}</p>
580
+ </div>
581
+ <div class="text-right">
582
+ <span class="inline-block bg-gray-100 text-gray-800 text-sm px-3 py-1 rounded-full">
583
+ ${item.recordCount} saham
584
+ </span>
585
+ <div class="mt-2 space-x-2">
586
+ <button onclick="loadFromDatabase(${item.id})" class="text-blue-600 hover:text-blue-800 text-sm">
587
+ <i class="fas fa-chart-bar"></i> Lihat
588
+ </button>
589
+ <button onclick="deleteFromDatabase(${item.id})" class="text-red-600 hover:text-red-800 text-sm">
590
+ <i class="fas fa-trash"></i> Hapus
591
+ </button>
592
+ </div>
593
+ </div>
594
+ </div>
595
+ `;
596
+ container.appendChild(itemDiv);
597
+ });
598
+ }
599
+
600
+ // Load from database
601
+ function loadFromDatabase(id) {
602
+ const item = database.find(d => d.id === id);
603
+ if (!item) return;
604
+
605
+ alert(`Fitur lengkap akan diintegrasikan. Saat ini, fungsi ini akan memuat data laporan ${item.monthName} ${item.year}`);
606
+
607
+ // This would load the actual CSV data from IndexedDB or localStorage
608
+ // For demo, we're just showing the alert
609
+ }
610
+
611
+ // Delete from database
612
+ function deleteFromDatabase(id) {
613
+ if (confirm('Apakah Anda yakin ingin menghapus laporan ini dari database?')) {
614
+ database = database.filter(d => d.id !== id);
615
+ localStorage.setItem('ownershipDatabase', JSON.stringify(database));
616
+ renderDatabase();
617
+ }
618
+ }
619
+
620
+ // Export analysis
621
+ function exportAnalysis() {
622
+ if (processedData.length === 0) {
623
+ alert('Tidak ada data untuk diekspor. Silakan unggah dan proses data terlebih dahulu.');
624
+ return;
625
+ }
626
+
627
+ // Prepare export data
628
+ const exportData = processedData.map(row => ({
629
+ 'Ticker': row.Ticker,
630
+ 'Nama Saham': row['Nama Saham'],
631
+ 'Kepemilikan Lokal (%)': formatNumber(row.Lokal),
632
+ 'Kepemilikan Asing (%)': formatNumber(row.Asing),
633
+ 'Kepemilikan Institusi (%)': formatNumber(row.Institusi),
634
+ 'Tanggal': row.Tanggal,
635
+ 'Kategori': row.Asing > 0 ? 'Net Buy' : 'Net Sell'
636
+ }));
637
+
638
+ // Convert to CSV
639
+ const csv = Papa.unparse(exportData);
640
+
641
+ // Create download link
642
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
643
+ const url = URL.createObjectURL(blob);
644
+ const link = document.createElement('a');
645
+ link.setAttribute('href', url);
646
+ link.setAttribute('download', 'analisis-kepemilikan-saham.csv');
647
+ link.style.visibility = 'hidden';
648
+ document.body.appendChild(link);
649
+ link.click();
650
+ document.body.removeChild(link);
651
+ }
652
+ </script>
653
+ <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/ksei" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
654
+ </html>
prompts.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ tolong buatkan analitic tool untuk mendownload data dari web tentang kenaikan kepemilikan sahan baik lokal , asing atau institusi dari hasil web berikut https://web.ksei.co.id/Download/ sehingga dapat di analisa secara dashboard dan aplikasi berbasis csv sehingga mudah di ketahui dan di interpretasikan
2
+ tolong tambahkan informasi pemisah kolom adalah | . dan tambahkan fitur penyimpanan database bulan berapa tahun berapa