Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions church_asset_tool/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
const assetForm = document.getElementById('assetForm');
const assetTableBody = document.querySelector('#assetTable tbody');
const exportCsvBtn = document.getElementById('exportCsv');
const exportXlsxBtn = document.getElementById('exportXlsx');
const importFile = document.getElementById('importFile');

let assets = JSON.parse(localStorage.getItem('assets') || '[]');
renderTable();

assetForm.addEventListener('submit', async (e) => {
e.preventDefault();
const type = document.getElementById('type').value;
const name = document.getElementById('name').value;
const description = document.getElementById('description').value;
const photoInput = document.getElementById('photo');

let photoData = '';
if (photoInput.files[0]) {
photoData = await toBase64(photoInput.files[0]);
}

const asset = { type, name, description, photoData };
assets.push(asset);
localStorage.setItem('assets', JSON.stringify(assets));
renderTable();
assetForm.reset();
});

exportCsvBtn.addEventListener('click', () => {
const csv = Papa.unparse(assets.map(a => ({
type: a.type,
name: a.name,
description: a.description,
photoData: a.photoData
})));
downloadFile(csv, 'assets.csv', 'text/csv;charset=utf-8;');
});

exportXlsxBtn.addEventListener('click', () => {
const ws = XLSX.utils.json_to_sheet(assets.map(a => ({
type: a.type,
name: a.name,
description: a.description,
photoData: a.photoData
})));
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Assets');
const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
downloadFile(new Blob([wbout], { type: 'application/octet-stream' }), 'assets.xlsx');
});

importFile.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
const isCsv = file.name.endsWith('.csv');
reader.onload = (evt) => {
if (isCsv) {
const parsed = Papa.parse(evt.target.result, { header: true }).data;
assets = parsed.filter(r => r.type).map(r => ({
type: r.type,
name: r.name,
description: r.description,
photoData: r.photoData
}));
} else {
const wb = XLSX.read(evt.target.result, { type: 'binary' });
const ws = wb.Sheets[wb.SheetNames[0]];
const parsed = XLSX.utils.sheet_to_json(ws);
assets = parsed.map(r => ({
type: r.type,
name: r.name,
description: r.description,
photoData: r.photoData
}));
}
localStorage.setItem('assets', JSON.stringify(assets));
renderTable();
};
if (isCsv) {
reader.readAsText(file);
} else {
reader.readAsBinaryString(file);
}
});

function renderTable() {
assetTableBody.innerHTML = '';
assets.forEach(asset => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${asset.type}</td>
<td>${asset.name}</td>
<td>${asset.description}</td>
<td>${asset.photoData ? `<img src="${asset.photoData}" class="thumbnail"/>` : ''}</td>
`;
assetTableBody.appendChild(tr);
});
}

function toBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
reader.readAsDataURL(file);
});
}

function downloadFile(data, filename, type) {
const blob = data instanceof Blob ? data : new Blob([data], { type });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}
48 changes: 48 additions & 0 deletions church_asset_tool/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>교회 자산 관리</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>교회 자산 관리 도구</h1>
<form id="assetForm">
<label for="type">자산 유형</label>
<select id="type" required>
<option value="동산">동산</option>
<option value="부동산">부동산</option>
<option value="시설">교회시설</option>
</select>
<label for="name">이름</label>
<input type="text" id="name" required />
<label for="description">설명</label>
<textarea id="description"></textarea>
<label for="photo">사진</label>
<input type="file" id="photo" accept="image/*" />
<button type="submit">추가</button>
</form>
<div class="actions">
<button id="exportCsv">CSV 내보내기</button>
<button id="exportXlsx">Excel 내보내기</button>
<input type="file" id="importFile" />
</div>
<table id="assetTable">
<thead>
<tr>
<th>유형</th>
<th>이름</th>
<th>설명</th>
<th>사진</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script src="https://cdn.jsdelivr.net/npm/papaparse@5.4.1/papaparse.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
<script src="app.js"></script>
</body>
</html>
37 changes: 37 additions & 0 deletions church_asset_tool/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
}
.container {
max-width: 800px;
width: 100%;
padding: 20px;
}
form {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ccc;
padding: 8px;
text-align: left;
}
.actions {
margin-bottom: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
img.thumbnail {
max-width: 100px;
max-height: 100px;
}