adding new mindforge applications
All checks were successful
Mindforge API Build and Deploy / Build Mindforge API Image (push) Successful in 1m8s
Mindforge Cronjob Build and Deploy / Build Mindforge Cronjob Image (push) Successful in 1m19s
Mindforge API Build and Deploy / Deploy Mindforge API (internal) (push) Successful in 11s
Mindforge Cronjob Build and Deploy / Deploy Mindforge Cronjob (internal) (push) Successful in 10s
Mindforge Web Build and Deploy (internal) / Build Mindforge Web Image (push) Successful in 2m25s
Mindforge Web Build and Deploy (internal) / Deploy Mindforge Web (internal) (push) Successful in 12s

This commit is contained in:
2026-03-20 22:51:04 -03:00
parent 36e405a9a8
commit 3e09b03753
55 changed files with 4164 additions and 2 deletions

View File

@@ -0,0 +1,197 @@
import { useState, useRef } from 'preact/hooks';
import { MindforgeApiService } from '../services/MindforgeApiService';
import { Button } from './Button';
import * as diff from 'diff';
import { marked } from 'marked';
import './VerificadorComponent.css';
function utf8ToBase64(str: string): string {
const bytes = new TextEncoder().encode(str);
const binary = Array.from(bytes, byte => String.fromCharCode(byte)).join('');
return window.btoa(binary);
}
type CheckTypeEnum = 'language' | 'content' | 'both';
export function VerificadorComponent() {
const [text, setText] = useState('');
const [fileName, setFileName] = useState('manual_input.md');
const [checkType, setCheckType] = useState<CheckTypeEnum>('language');
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [languageResult, setLanguageResult] = useState<string | null>(null);
const [contentResult, setContentResult] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleFileUpload = (e: Event) => {
const target = e.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
const file = target.files[0];
setFileName(file.name);
const reader = new FileReader();
reader.onload = (event) => {
if (event.target?.result) {
setText(event.target.result as string);
}
};
reader.readAsText(file);
}
};
const handleSubmit = async () => {
if (!text.trim()) {
setError('Por favor, insira algum texto ou faça upload de um arquivo.');
return;
}
setLoading(true);
setError(null);
setLanguageResult(null);
setContentResult(null);
const base64Content = utf8ToBase64(text);
try {
if (checkType === 'both') {
const [langRes, contRes] = await Promise.all([
MindforgeApiService.checkFile({ fileContent: base64Content, fileName, checkType: 'language' }),
MindforgeApiService.checkFile({ fileContent: base64Content, fileName, checkType: 'content' })
]);
setLanguageResult(langRes.result);
setContentResult(contRes.result);
} else {
const res = await MindforgeApiService.checkFile({ fileContent: base64Content, fileName, checkType });
if (checkType === 'language') setLanguageResult(res.result);
else setContentResult(res.result);
}
} catch (err: any) {
setError(err.message || 'Ocorreu um erro ao processar sua requisição.');
} finally {
setLoading(false);
}
};
const renderDiff = (original: string, updated: string) => {
const diffResult = diff.diffWordsWithSpace(original, updated);
return (
<div className="diff-view">
{diffResult.map((part, index) => {
const className = part.added ? 'diff-added' : part.removed ? 'diff-removed' : '';
return (
<span key={index} className={className}>
{part.value}
</span>
);
})}
</div>
);
};
return (
<div className="verificador-container">
<h2 className="title" style={{ fontSize: '2.5rem' }}>Verificador de Arquivos</h2>
<p className="subtitle">Faça o upload do seu arquivo Markdown para validação de linguagem ou conteúdo.</p>
<div className="verificador-form">
<div className="input-group">
<label>Texto (Markdown)</label>
<textarea
className="text-area"
value={text}
onInput={(e) => setText((e.target as HTMLTextAreaElement).value)}
placeholder="Cole seu texto aqui ou faça upload de um arquivo..."
/>
</div>
<div className="file-input-wrapper">
<input
type="file"
accept=".md,.txt"
ref={fileInputRef}
onChange={handleFileUpload}
className="file-input"
id="verificador-file"
/>
<label htmlFor="verificador-file" className="file-input-label">
📁 Escolher Arquivo
</label>
<span style={{ fontSize: '0.9rem', color: 'rgba(255,255,255,0.6)' }}>
{fileName !== 'manual_input.md' ? fileName : 'Nenhum arquivo selecionado'}
</span>
</div>
<div className="input-group">
<label>Tipo de Verificação</label>
<select
className="select-input"
value={checkType}
onChange={(e) => setCheckType((e.target as HTMLSelectElement).value as CheckTypeEnum)}
>
<option value="language">Linguagem</option>
<option value="content">Conteúdo</option>
<option value="both">Linguagem e Conteúdo</option>
</select>
</div>
<Button variant="primary" onClick={handleSubmit} disabled={loading} style={{ marginTop: '1rem' }}>
{loading ? 'Processando...' : 'Verificar'}
</Button>
{error && <div style={{ color: '#ff7b72', marginTop: '1rem' }}>{error}</div>}
</div>
{loading && (
<div className="spinner-container">
<div className="spinner"></div>
<p>Analisando sua forja mental...</p>
</div>
)}
{/* Render Results */}
{!loading && (languageResult || contentResult) && (
<div className="response-section">
{checkType === 'language' && languageResult && (
<div className="side-pane">
<div className="pane-title">Resultado - Linguagem (Diff)</div>
<div className="response-content">
{renderDiff(text, languageResult)}
</div>
</div>
)}
{checkType === 'content' && contentResult && (
<div className="side-pane">
<div className="pane-title">Resultado - Conteúdo</div>
<div
className="response-content markdown-body"
dangerouslySetInnerHTML={{ __html: marked.parse(contentResult) as string }}
/>
</div>
)}
{checkType === 'both' && languageResult && contentResult && (
<div className="side-by-side">
<div className="side-pane">
<div className="pane-title">Linguagem (Diff)</div>
<div className="response-content" style={{ minHeight: '300px' }}>
{renderDiff(text, languageResult)}
</div>
</div>
<div className="side-pane">
<div className="pane-title">Conteúdo</div>
<div
className="response-content markdown-body"
style={{ minHeight: '300px' }}
dangerouslySetInnerHTML={{ __html: marked.parse(contentResult) as string }}
/>
</div>
</div>
)}
</div>
)}
</div>
);
}