217 lines
5.6 KiB
Svelte
217 lines
5.6 KiB
Svelte
<script lang="ts">
|
|
import { createEventDispatcher } from 'svelte';
|
|
import type { BicorderState } from '../types';
|
|
|
|
export let data: BicorderState;
|
|
export let focusedMode: boolean = false;
|
|
|
|
const dispatch = createEventDispatcher<{
|
|
reset: void;
|
|
}>();
|
|
|
|
let showUploadDialog = false;
|
|
let isUploading = false;
|
|
|
|
// Gitea configuration
|
|
const GITEA_TOKEN = 'd495e72e955c00be2de0f1e18183f6a385b6e52c';
|
|
const GITEA_API_URL = 'https://git.medlab.host/api/v1';
|
|
const REPO_OWNER = 'ntnsndr';
|
|
const REPO_NAME = 'protocol-bicorder-data';
|
|
const REPO_URL = `https://git.medlab.host/${REPO_OWNER}/${REPO_NAME}`;
|
|
const APP_VERSION = '1.0.0';
|
|
|
|
function exportToJSON() {
|
|
const json = JSON.stringify(data, null, 2);
|
|
const blob = new Blob([json], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `bicorder-${data.metadata.protocol || 'diagnostic'}-${new Date().toISOString().slice(0, 10)}.json`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
async function uploadReadings() {
|
|
isUploading = true;
|
|
|
|
try {
|
|
// Generate filename with timestamp
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5) + 'Z';
|
|
const filename = `bicorder-${timestamp}.json`;
|
|
const filepath = `readings/${filename}`;
|
|
|
|
// Create commit message
|
|
const protocolName = data.metadata.protocol || 'Unknown Protocol';
|
|
const analystName = data.metadata.analyst || 'Anonymous';
|
|
const commitMessage = `Bicorder reading: ${protocolName} by ${analystName} | Source: Protocol Bicorder v${data.version}`;
|
|
|
|
// Prepare the content (base64 encoded)
|
|
const jsonContent = JSON.stringify(data, null, 2);
|
|
const base64Content = btoa(unescape(encodeURIComponent(jsonContent)));
|
|
|
|
// Get current timestamp for commit
|
|
const now = new Date();
|
|
const commitDate = now.toISOString();
|
|
|
|
// Upload to Gitea
|
|
const response = await fetch(
|
|
`${GITEA_API_URL}/repos/${REPO_OWNER}/${REPO_NAME}/contents/${filepath}`,
|
|
{
|
|
method: 'POST',
|
|
mode: 'cors',
|
|
headers: {
|
|
'Authorization': `token ${GITEA_TOKEN}`,
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
content: base64Content,
|
|
message: commitMessage,
|
|
branch: 'main',
|
|
dates: {
|
|
author: commitDate,
|
|
committer: commitDate,
|
|
},
|
|
}),
|
|
}
|
|
);
|
|
|
|
if (response.ok) {
|
|
alert('Successfully uploaded! Your reading is now public.');
|
|
showUploadDialog = false;
|
|
} else {
|
|
const errorData = await response.json();
|
|
throw new Error(errorData.message || `Upload failed: ${response.statusContents}`);
|
|
}
|
|
} catch (err) {
|
|
console.error('Upload error:', err);
|
|
alert(`Upload error: ${(err as Error).message}`);
|
|
} finally {
|
|
isUploading = false;
|
|
}
|
|
}
|
|
|
|
function handleReset() {
|
|
dispatch('reset');
|
|
}
|
|
</script>
|
|
|
|
<section class="export-controls" class:focused={focusedMode}>
|
|
<div class="button-group">
|
|
<button on:click={exportToJSON}>
|
|
💾 Export JSON
|
|
</button>
|
|
|
|
<button on:click={() => showUploadDialog = !showUploadDialog}>
|
|
📤 Upload
|
|
</button>
|
|
|
|
<button class="reset-btn" on:click={handleReset}>
|
|
🗑️ Reset All
|
|
</button>
|
|
</div>
|
|
|
|
{#if showUploadDialog}
|
|
<div class="webhook-config">
|
|
<p class="upload-confirmation">
|
|
Are you sure you are ready to share your readings publicly?
|
|
</p>
|
|
<p class="upload-terms">
|
|
Submitted readings are posted publicly and licensed to the public domain. By proceeding, you agree to these terms.
|
|
</p>
|
|
<p class="upload-repo-link">
|
|
Readings are posted to <a href={REPO_URL} target="_blank" rel="noopener noreferrer">this repository</a>.
|
|
</p>
|
|
<div class="webhook-buttons">
|
|
<button on:click={uploadReadings} disabled={isUploading}>
|
|
{isUploading ? 'Uploading...' : 'Proceed'}
|
|
</button>
|
|
<button on:click={() => showUploadDialog = false} disabled={isUploading}>Cancel</button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</section>
|
|
|
|
<style>
|
|
.export-controls {
|
|
margin: 2rem 0;
|
|
padding: 1rem;
|
|
border-top: 2px solid var(--border-color);
|
|
}
|
|
|
|
.export-controls.focused {
|
|
border-top: none;
|
|
}
|
|
|
|
.button-group {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
gap: 0.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.reset-btn {
|
|
border-color: #ff0000;
|
|
color: #ff0000;
|
|
}
|
|
|
|
.reset-btn:hover {
|
|
background-color: #ff0000;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.webhook-config {
|
|
margin-top: 1rem;
|
|
padding: 1rem;
|
|
border: 2px solid var(--border-color);
|
|
}
|
|
|
|
.upload-confirmation {
|
|
font-size: 1rem;
|
|
font-weight: bold;
|
|
margin-bottom: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.upload-terms {
|
|
font-size: 0.85rem;
|
|
opacity: 0.7;
|
|
line-height: 1.4;
|
|
margin-bottom: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.upload-repo-link {
|
|
font-size: 0.9rem;
|
|
margin-bottom: 1rem;
|
|
text-align: center;
|
|
}
|
|
|
|
.upload-repo-link a {
|
|
color: var(--fg-color);
|
|
text-decoration: underline;
|
|
}
|
|
|
|
.upload-repo-link a:hover {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.webhook-buttons {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.webhook-buttons button {
|
|
flex: 1;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.button-group {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|