Portfólio · Nicolas Doboš · 2026
Pomáham firmám nahradiť Excel, manuálne kroky a neprehľadné workflow vlastným webovým systémom.
Výsledok: menej ručnej práce, lepšie procesy a riešenie, ktoré sa dá bezpečne rozvíjať aj o roky.
Pre firmy, ktoré už narážajú na limity tabuliek a ručných procesov
Slovensko
Keď sa procesy strácajú v tabuľkách, e-mailoch a poznámkach, navrhnem jedno centrálne riešenie.
Od zadania po nasadenie: portál, admin, oprávnenia, API napojenia a prehľadná prevádzka.
Riešim aj dlhodobý rozvoj, údržbu a rozširovanie systému podľa rastu firmy.
Premýšľam nad riešením ako nad nástrojom pre biznis, nie len ako nad kódom. Každý modul má mať jasný dopad: menej chýb, menej ručnej práce, lepší prehľad.
Architektúru navrhujem tak, aby sa systém dal bezpečne rozvíjať aj o rok či dva, bez bolestivého prepisovania od nuly.
Komunikujem priamo, technické veci prekladám do zrozumiteľného jazyka a držím sa dohodnutého rozsahu.
Stack
Technológie podľa typu projektu, od frontendu cez backend až po nasadenie a prevádzku.
Keď máte procesy roztrúsené v mailoch, Exceli a ručných krokoch. Dodám jednotnú aplikáciu, ktorá ich prepojí do jedného workflow.
Keď tím rastie a starý spôsob práce už nestačí. CRM, objednávkové toky, reporty a integrácie nastavím tak, aby systém podporoval každodennú prevádzku.
Keď nechcete len rýchly MVP web, ale riešenie, ktoré vydrží. Od návrhu po ďalší rozvoj, údržbu a plánované rozšírenia.
Prejdeme váš proces, ciele a slabé miesta. Zistíme, či je vlastný systém vhodné riešenie.
Dostanete jasný návrh architektúry, rozsahu a priorít, aby ste vedeli čo sa bude robiť a prečo.
Dodávam priebežne, nie až na konci. Vidíte výsledok skôr a vieme iterovať bez chaosu.
Po nasadení pokračujeme podľa potreby: podpora, údržba a ďalšie funkcionality pre rast firmy.
Reálne ukážky z produkčného CRM systému: páry use-case súborov z backendu aj frontendu, ktoré spolu tvoria jedno workflow od API vrstvy až po React obrazovku.
<?php
namespace App\Services;
class AutoMailService
{
public function onOrderStatusChanged(Order $order, OrderStatusEnum $from, OrderStatusEnum $to): void
{
if ($from === $to || ! $this->settings->isTruthy('auto_mail.enabled')) {
return;
}
$suffix = $this->statusToConfigSuffix($to);
if ($suffix === null) {
return;
}
$prefix = "auto_mail.order_{$suffix}";
if (! $this->settings->isTruthy("{$prefix}.enabled")) {
return;
}
$order->loadMissing(['client', 'user']);
$client = $order->client;
if (! $client instanceof Client || ! filled($client->email) || ! filter_var($client->email, FILTER_VALIDATE_EMAIL)) {
return;
}
$map = $this->placeholderMap($order);
$subject = trim((string) $this->settings->get("{$prefix}.subject"));
$body = trim((string) $this->settings->get("{$prefix}.body"));
$subject = $this->replacePlaceholders($subject, $map);
$body = $this->replacePlaceholders($body, $map);
Mail::to($client->email)->queue(new AutomatedClientMail($subject, $body));
}
} <?php
namespace App\Services;
final class OrderService
{
public function __construct(
private readonly OrderRepositoryInterface $orders,
private readonly OrderHandlerResolver $handlerResolver,
) {}
public function create(array $data): Order
{
$data['user_id'] = $data['user_id'] ?? auth()->id();
$data['order_number'] = $data['order_number'] ?? $this->generateOrderNumber();
if (! array_key_exists('status', $data) || $data['status'] === null) {
$data['status'] = OrderStatusEnum::NAVRH;
}
return $this->orders->create($data);
}
public function timeline(Order $order): array
{
$items = [];
$items[] = ['at' => $order->created_at->toIso8601String(), 'label' => 'Zákazka vytvorená', 'type' => 'order'];
foreach ($order->quotes()->latest()->get() as $quote) {
$items[] = ['at' => $quote->created_at->toIso8601String(), 'label' => 'Ponuka ' . $quote->quote_number, 'type' => 'quote'];
}
usort($items, fn ($a, $b) => strcmp($b['at'], $a['at']));
return $items;
}
} import { clearToken, getToken } from './auth';
const base = () => import.meta.env.PUBLIC_API_URL?.replace(/\/$/, '') ?? 'http://127.0.0.1:8000';
export async function apiRequest<T>(method: string, endpoint: string, body?: unknown): Promise<T> {
const token = getToken();
const headers: HeadersInit = {
Accept: 'application/json',
...(body && !(body instanceof FormData) ? { 'Content-Type': 'application/json' } : {}),
...(token ? { Authorization: `Bearer ${token}` } : {}),
};
const res = await fetch(`${base()}/api${endpoint}`, {
method,
headers,
body: body instanceof FormData ? body : body ? JSON.stringify(body) : undefined,
});
if (res.status === 401) clearToken();
if (!res.ok) throw new Error('API request failed');
if (res.status === 204) return undefined as T;
return (await res.json()) as T;
} export default function OrderDetail({ orderId }: { orderId: string }) {
const [order, setOrder] = useState<OrderDetail | null>(null);
const [timeline, setTimeline] = useState<TimelineItem[]>([]);
const [loading, setLoading] = useState(true);
async function reload() {
const raw = await api.get<unknown>(`/orders/${orderId}`);
const o = unwrapLaravelResource<OrderDetail>(raw);
setOrder(o);
}
useEffect(() => {
(async () => {
try {
await reload();
const tl = await api.get<{ data: TimelineItem[] }>(`/orders/${orderId}/timeline`);
setTimeline(tl.data);
} finally {
setLoading(false);
}
})();
}, [orderId]);
if (loading) return <p>Načítavam…</p>;
return <OrderDetailView order={order} timeline={timeline} />;
} Bez záväzku si prejdeme váš proces a zistíme, či vám vlastný systém reálne ušetrí čas a náklady.