refactor: complete application rewrite with modern UI

This commit is contained in:
sokol
2026-02-19 22:55:26 +03:00
parent 271b530fa1
commit a6cc5a9827
26 changed files with 3036 additions and 794 deletions

View File

@@ -1,41 +1,57 @@
import { Env } from "../models/Env";
import { ConfigReader } from "../models/ConfigReader";
import { Config } from "../models/Config";
import { ConfigBuilder } from "../builders/ConfigBuilder";
import { useRef } from 'react';
import { Upload, Download, FilePlus, File } from 'lucide-react';
import { Button } from '../components/ui';
import { Config } from '../models/Config';
import { ConfigReader } from '../models/ConfigReader';
import { ConfigBuilder } from '../builders/ConfigBuilder';
import { Env } from '../models/Env';
export function FileChooser(props: { onSelected: (x: Config) => void, config?: Config }) {
async function handleFile(x: React.ChangeEvent<HTMLInputElement>) {
let file = x.target.files![0];
interface FileChooserProps {
onSelected: (config: Config) => void;
config?: Config;
}
console.log(file.name, file.type, file.size, "supported:", ConfigReader.isSupportedFormat(file));
let reader = new ConfigReader();
let cfg = await reader.parseFromFile(file);
export function FileChooser({ onSelected, config }: FileChooserProps) {
const fileInputRef = useRef<HTMLInputElement>(null);
async function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
const file = event.target.files?.[0];
if (!file) return;
console.log(file.name, file.type, file.size, 'supported:', ConfigReader.isSupportedFormat(file));
const reader = new ConfigReader();
const cfg = await reader.parseFromFile(file);
if (cfg !== null) {
props.onSelected(cfg);
onSelected(cfg);
}
// Reset input
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
}
function handleNew(){
let cfg = new Config();
cfg.addEnvs([new Env(0, "DEFAULT", [])]);
cfg.addTemplate("{}");
props.onSelected(cfg);
function handleNew() {
const cfg = new Config();
cfg.setEnvs([new Env(0, 'DEFAULT', [])]);
cfg.setTemplate('{}');
onSelected(cfg);
}
function handleDownload() {
if (!props.config) {
alert("No configuration loaded");
if (!config) {
alert('No configuration loaded');
return;
}
const xmlContent = ConfigBuilder.buildFullXml(props.config);
const xmlContent = ConfigBuilder.buildFullXml(config);
const filename = ConfigBuilder.generateFilename();
// Create blob and download
const blob = new Blob([xmlContent], { type: "text/xml" });
const blob = new Blob([xmlContent], { type: 'text/xml' });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
@@ -45,25 +61,60 @@ export function FileChooser(props: { onSelected: (x: Config) => void, config?: C
}
return (
<>
<div className="col-2">
<button className="btn btn-primary" onClick={handleNew} >Create new</button>
</div>
<div className="col-auto">
<button
className="btn btn-success"
onClick={handleDownload}
disabled={!props.config}
title="Download full config template"
>
Download
</button>
</div>
<div className="col-1">or</div>
<div className="bg-white rounded-xl shadow-md p-4 border border-slate-200">
<div className="flex items-center gap-4 flex-wrap">
{/* Logo/Brand */}
<div className="flex items-center gap-2">
<div className="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center">
<File className="w-6 h-6 text-white" />
</div>
<span className="font-bold text-xl text-slate-800">Configucci</span>
</div>
<div className="col">
<input className="form-control" type="file" id="formFile" onChange={handleFile} />
</div >
</>
<div className="h-8 w-px bg-slate-200" />
{/* Action Buttons */}
<div className="flex items-center gap-2 flex-1">
<Button
variant="primary"
onClick={handleNew}
icon={FilePlus}
size="sm"
>
New Config
</Button>
<Button
variant="success"
onClick={handleDownload}
icon={Download}
size="sm"
disabled={!config}
title="Download full config template"
>
Download
</Button>
<span className="text-slate-400 text-sm">or</span>
{/* File Upload */}
<div className="flex-1">
<label
className="flex items-center justify-center gap-2 px-4 py-2 border-2 border-dashed border-slate-300 rounded-lg cursor-pointer hover:border-blue-400 hover:bg-blue-50 transition-all duration-200"
>
<Upload className="w-4 h-4 text-slate-400" />
<span className="text-sm text-slate-600">Upload XML Config</span>
<input
ref={fileInputRef}
type="file"
className="hidden"
accept=".xml,text/xml"
onChange={handleFileChange}
/>
</label>
</div>
</div>
</div>
</div>
);
}