grantforge-api / frontend-react /src /pages /CompanyProfile.tsx
GrantForge Bot
Deploy sha-93ab5d396350b3e6db35d3bd315ec00724aa6c32 — source build (no GHCR)
5ecb84c
Raw
History Blame Contribute Delete
12.2 kB
import React, { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Building2, Save, CheckCircle, AlertCircle, RefreshCw, Briefcase } from 'lucide-react';
import toast from 'react-hot-toast';
import { getCompanyProfile, updateCompanyProfile, CompanyProfileData } from '../api/client';
import { SkeletonLoader } from '../components/common/SkeletonLoader';
const CompanyProfile: React.FC = () => {
const [profile, setProfile] = useState<CompanyProfileData>({
nip: '',
name: '',
pkd_codes: [],
company_size: 'mikro',
location: '',
description: ''
});
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [pkdInput, setPkdInput] = useState('');
useEffect(() => {
fetchProfile();
}, []);
const fetchProfile = async () => {
setLoading(true);
try {
const data = await getCompanyProfile();
setProfile({
nip: data.nip || '',
name: data.name || '',
pkd_codes: data.pkd_codes || [],
company_size: data.company_size || 'mikro',
location: data.location || '',
description: data.description || '',
is_verified: data.is_verified || false
});
setPkdInput((data.pkd_codes || []).join(', '));
} catch (error) {
console.error("Błąd ładowania profilu", error);
toast.error("Nie udało się załadować danych firmy");
} finally {
setLoading(false);
}
};
const handleSave = async (e: React.FormEvent) => {
e.preventDefault();
if (!profile.nip) {
toast.error("NIP jest wymagany");
return;
}
setSaving(true);
try {
// Przetwarzanie PKD
const pkds = pkdInput.split(',')
.map(p => p.trim())
.filter(p => p.length > 0);
const updatedProfile = await updateCompanyProfile({
...profile,
pkd_codes: pkds
});
setProfile(updatedProfile);
setPkdInput((updatedProfile.pkd_codes || []).join(', '));
toast.success("Zapisano profil firmy");
} catch (error) {
console.error("Błąd zapisywania", error);
toast.error("Nie udało się zapisać profilu");
} finally {
setSaving(false);
}
};
if (loading) {
return (
<div style={{ padding: '2rem 3rem', maxWidth: '1000px', margin: '0 auto', width: '100%' }}>
<SkeletonLoader count={1} height="80px" style={{ marginBottom: '2rem' }} />
<SkeletonLoader count={4} height="60px" style={{ marginBottom: '1rem' }} />
<SkeletonLoader count={1} height="120px" />
</div>
);
}
return (
<div style={{ padding: '2rem 3rem', maxWidth: '1000px', margin: '0 auto', width: '100%', height: '100%', overflowY: 'auto' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '3rem' }}>
<div>
<h1 className="display-font" style={{ fontSize: '2.5rem', color: 'var(--text-primary)', marginBottom: '0.5rem', display: 'flex', alignItems: 'center', gap: '0.8rem' }}>
<Building2 size={36} color="var(--accent-blue)" />
Mój Profil Firmy
</h1>
<p style={{ color: 'var(--text-secondary)', fontSize: '1.05rem', marginLeft: '3.4rem' }}>
Zapisz dane swojego przedsiębiorstwa, aby automatycznie dopasowywać naboru i generować precyzyjniejsze wnioski.
</p>
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="glass-card"
style={{ padding: '2.5rem' }}
>
{profile.is_verified ? (
<div style={{ background: 'rgba(16, 185, 129, 0.1)', border: '1px solid rgba(16, 185, 129, 0.3)', padding: '1rem', borderRadius: '8px', marginBottom: '2rem', display: 'flex', alignItems: 'center', gap: '0.8rem', color: 'var(--accent-green)' }}>
<CheckCircle size={20} />
<div>
<strong>Dane firmy zweryfikowane</strong>
<p style={{ margin: 0, fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Status MŚP dla tego numeru NIP został automatycznie potwierdzony.</p>
</div>
</div>
) : profile.nip ? (
<div style={{ background: 'rgba(239, 68, 68, 0.1)', border: '1px solid rgba(239, 68, 68, 0.3)', padding: '1rem', borderRadius: '8px', marginBottom: '2rem', display: 'flex', alignItems: 'center', gap: '0.8rem', color: '#ef4444' }}>
<AlertCircle size={20} />
<div>
<strong>Brak weryfikacji statusu MŚP</strong>
<p style={{ margin: 0, fontSize: '0.9rem', color: 'var(--text-secondary)' }}>Zapisz poprawne dane, aby system dokonał walidacji w KRS/CEIDG.</p>
</div>
</div>
) : null}
<form onSubmit={handleSave} style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
<div>
<label style={{ display: 'block', color: 'var(--text-secondary)', marginBottom: '0.5rem', fontWeight: 600 }}>Numer NIP *</label>
<input
type="text"
value={profile.nip}
onChange={(e) => setProfile({...profile, nip: e.target.value})}
placeholder="Np. 5213214567"
required
style={{ width: '100%', padding: '1rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }}
/>
</div>
<div>
<label style={{ display: 'block', color: 'var(--text-secondary)', marginBottom: '0.5rem', fontWeight: 600 }}>Nazwa Firmy</label>
<input
type="text"
value={profile.name || ''}
onChange={(e) => setProfile({...profile, name: e.target.value})}
placeholder="Pełna nazwa przedsiębiorstwa"
style={{ width: '100%', padding: '1rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }}
/>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1.5rem' }}>
<div>
<label style={{ display: 'block', color: 'var(--text-secondary)', marginBottom: '0.5rem', fontWeight: 600 }}>Status Przedsiębiorstwa (MŚP)</label>
<select
value={profile.company_size || 'mikro'}
onChange={(e) => setProfile({...profile, company_size: e.target.value})}
style={{ width: '100%', padding: '1rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', outline: 'none' }}
>
<option value="mikro" style={{ background: '#0B0E14' }}>Mikroprzedsiębiorstwo</option>
<option value="mala" style={{ background: '#0B0E14' }}>Małe przedsiębiorstwo</option>
<option value="srednia" style={{ background: '#0B0E14' }}>Średnie przedsiębiorstwo</option>
<option value="duza" style={{ background: '#0B0E14' }}>Duże przedsiębiorstwo (Small Mid-caps/Mid-caps)</option>
</select>
</div>
<div>
<label style={{ display: 'block', color: 'var(--text-secondary)', marginBottom: '0.5rem', fontWeight: 600 }}>Kody PKD (po przecinku)</label>
<div style={{ position: 'relative' }}>
<Briefcase size={18} color="var(--text-muted)" style={{ position: 'absolute', top: '50%', transform: 'translateY(-50%)', left: '1rem' }} />
<input
type="text"
value={pkdInput}
onChange={(e) => setPkdInput(e.target.value)}
placeholder="Np. 62.01.Z, 72.19.Z"
style={{ width: '100%', padding: '1rem 1rem 1rem 3rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }}
/>
</div>
</div>
</div>
<div>
<label style={{ display: 'block', color: 'var(--text-secondary)', marginBottom: '0.5rem', fontWeight: 600 }}>Lokalizacja (Województwo)</label>
<input
type="text"
value={profile.location || ''}
onChange={(e) => setProfile({...profile, location: e.target.value})}
placeholder="Np. mazowieckie"
style={{ width: '100%', padding: '1rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff' }}
/>
</div>
<div>
<label style={{ display: 'block', color: 'var(--text-secondary)', marginBottom: '0.5rem', fontWeight: 600 }}>Opis Działalności (Krótki profil technologiczny / obszar innowacji)</label>
<textarea
value={profile.description || ''}
onChange={(e) => setProfile({...profile, description: e.target.value})}
placeholder="Czym zajmuje się firma? Jakie innowacje wprowadza? Ten opis pomoże w automatycznym generowaniu sekcji."
style={{ width: '100%', padding: '1rem', background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.1)', borderRadius: '8px', color: '#fff', minHeight: '120px', resize: 'vertical' }}
/>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '1rem' }}>
<button
type="submit"
disabled={saving}
className="btn btn-primary"
style={{ padding: '1rem 2rem', fontSize: '1.05rem', background: 'var(--accent-blue)', display: 'flex', alignItems: 'center', gap: '0.5rem', border: 'none', color: '#fff', fontWeight: 600 }}
>
{saving ? <RefreshCw size={20} className="spin" /> : <Save size={20} />}
{saving ? 'Zapisywanie...' : 'Zapisz Profil Firmy'}
</button>
</div>
</form>
</motion.div>
</div>
);
};
export default CompanyProfile;