Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0ac1979
chore(ci): add lint and typecheck to workflows
omerfarukbolat Dec 26, 2025
612f3a9
chore(ci): add lint and typecheck scripts
omerfarukbolat Dec 26, 2025
a607a32
fix(lint): resolve all eslint errors and warnings
omerfarukbolat Dec 26, 2025
950c015
fix(types): fix all typecheck errors
omerfarukbolat Dec 26, 2025
3c0f53e
fix(ci): fix workflows
omerfarukbolat Dec 26, 2025
bfa5247
chore(ci): switch CI back to Bun for install, lint, and typecheck
omerfarukbolat Dec 26, 2025
7f2c925
chore: update bun.lock to match package.json dependencies
omerfarukbolat Dec 26, 2025
aaa6a27
chore(ci): fix SonarCloud configuration and authentication
omerfarukbolat Dec 26, 2025
d8527ec
fix(ci): show SonarCloud job even when token is missing
omerfarukbolat Dec 26, 2025
0f76827
fix(ci): ensure CI checks always show success status
omerfarukbolat Dec 26, 2025
e1551b2
Merge pull request #1 from omerfarukbolat/chore/ci-lint-typecheck
omerfarukbolat Dec 26, 2025
ad9196f
chore(ci): remove SonarCloud analysis
omerfarukbolat Dec 26, 2025
a73cf6e
feat(ci): restore SonarCloud analysis job
omerfarukbolat Dec 26, 2025
40ed546
chore(ci): switch Docker workflow to Bun
omerfarukbolat Dec 26, 2025
3d85083
refactor: replace all 'any' types with proper TypeScript types
omerfarukbolat Dec 26, 2025
08af59e
fix: add null safety checks to SchemaDiagram component
omerfarukbolat Dec 29, 2025
64b441c
fix: pin React version to 19.2.1
omerfarukbolat Dec 29, 2025
5f6a808
fix: resolve merge conflicts and fix lint/typecheck errors
omerfarukbolat Dec 31, 2025
b3a0116
fix: resolve merge conflicts and fix all TypeScript/ESLint errors
omerfarukbolat Dec 31, 2025
9971d8c
fix(monitoring): run connection init effect on mount only
omerfarukbolat Jan 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:

jobs:
lint-and-build:
name: Lint and Build
name: Lint, Typecheck and Build
runs-on: ubuntu-latest

steps:
Expand All @@ -27,6 +27,9 @@ jobs:
- name: Run ESLint
run: bun run lint

- name: Run TypeScript type check
run: bun run typecheck

- name: Build project
run: bun run build
env:
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/docker-build-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,28 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Run ESLint
run: bun run lint

- name: Run TypeScript type check
run: bun run typecheck

- name: Extract version from package.json or branch name
id: extract-version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ github.event.inputs.version }}"
elif [[ "${GITHUB_REF}" == "refs/heads/main" ]]; then
# Get version from package.json for main branch
VERSION=$(node -p "require('./package.json').version")
VERSION=$(bun -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8')); console.log(pkg.version)")
echo "Using package.json version: ${VERSION}"
else
# Extract version from release branch name
Expand Down
10 changes: 5 additions & 5 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
"next": "^15.5.7",
"next-themes": "^0.4.6",
"pg": "^8.16.3",
"react": "^19.2.1",
"react": "19.2.1",
"react-day-picker": "^9.11.2",
"react-dom": "^19.2.1",
"react-dom": "19.2.1",
"react-hook-form": "^7.66.1",
"react-resizable-panels": "^3.0.6",
"recharts": "^2.15.4",
Expand Down Expand Up @@ -1050,11 +1050,11 @@

"randexp": ["randexp@0.4.6", "", { "dependencies": { "discontinuous-range": "1.0.0", "ret": "~0.1.10" } }, "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ=="],

"react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="],
"react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="],

"react-day-picker": ["react-day-picker@9.13.0", "", { "dependencies": { "@date-fns/tz": "^1.4.1", "date-fns": "^4.1.0", "date-fns-jalali": "^4.1.0-0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-euzj5Hlq+lOHqI53NiuNhCP8HWgsPf/bBAVijR50hNaY1XwjKjShAnIe8jm8RD2W9IJUvihDIZ+KrmqfFzNhFQ=="],

"react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="],
"react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="],

"react-hook-form": ["react-hook-form@7.69.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw=="],

Expand Down Expand Up @@ -1182,7 +1182,7 @@

"tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="],

"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
"ts-api-utils": ["ts-api-utils@2.3.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-6eg3Y9SF7SsAvGzRHQvvc1skDAhwI4YQ32ui1scxD1Ccr0G5qIIbUBT3pFTKX8kmWIQClHobtUdNuaBgwdfdWg=="],

"tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"dev": "next dev --turbopack",
"build": "next build",
"start": "next start",
"lint": "eslint"
"lint": "eslint .",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@google/generative-ai": "^0.24.1",
Expand Down Expand Up @@ -57,9 +58,9 @@
"next": "^15.5.7",
"next-themes": "^0.4.6",
"pg": "^8.16.3",
"react": "^19.2.1",
"react": "19.2.1",
"react-day-picker": "^9.11.2",
"react-dom": "^19.2.1",
"react-dom": "19.2.1",
"react-hook-form": "^7.66.1",
"react-resizable-panels": "^3.0.6",
"recharts": "^2.15.4",
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/auth/login/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export async function POST(request: NextRequest) {
{ success: false, message: 'Invalid password' },
{ status: 401 }
);
} catch (error) {
} catch {
return NextResponse.json(
{ success: false, message: 'An error occurred' },
{ status: 500 }
Expand Down
1 change: 0 additions & 1 deletion src/app/api/db/query/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import {
getOrCreateProvider,
DatabaseError,
QueryError,
TimeoutError,
isDatabaseError,
Expand Down
4 changes: 2 additions & 2 deletions src/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { Database, Lock, User, ShieldCheck, UserCheck } from 'lucide-react';
import { Database, Lock, ShieldCheck, UserCheck } from 'lucide-react';
import { toast } from 'sonner';
import { Badge } from '@/components/ui/badge';

Expand Down Expand Up @@ -41,7 +41,7 @@ export default function LoginPage() {
} else {
toast.error(data.message || 'Invalid password');
}
} catch (error) {
} catch {
toast.error('An error occurred. Please try again.');
} finally {
setIsLoading(false);
Expand Down
9 changes: 1 addition & 8 deletions src/components/ConnectionModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@/components/ui/select';
import { DatabaseConnection, DatabaseType } from '@/lib/types';
import { Cloud, HardDrive, Database, Cpu, ShieldCheck, Zap, Globe, Key } from 'lucide-react';
import { cn } from '@/lib/utils';
Expand Down Expand Up @@ -60,7 +53,7 @@ export function ConnectionModal({ isOpen, onClose, onConnect }: ConnectionModalP
setDatabase('');
};

const dbTypes: { value: DatabaseType, label: string, icon: any, color: string }[] = [
const dbTypes: { value: DatabaseType, label: string, icon: React.ComponentType<{ className?: string }>, color: string }[] = [
{ value: 'postgres', label: 'PostgreSQL', icon: Cloud, color: 'text-blue-400' },
{ value: 'mysql', label: 'MySQL', icon: HardDrive, color: 'text-amber-400' },
{ value: 'mongodb', label: 'MongoDB', icon: Database, color: 'text-emerald-400' },
Expand Down
6 changes: 2 additions & 4 deletions src/components/CreateTableModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ import {
Plus,
Trash2,
Table as TableIcon,
Key,
Type,
Settings2,
AlertCircle,
Loader2
} from 'lucide-react';
import { toast } from 'sonner';
Expand Down Expand Up @@ -60,12 +58,12 @@ const DATA_TYPES = [
'DECIMAL(10,2)',
];

export function CreateTableModal({ isOpen, onClose, onTableCreated, dbType = 'postgres' }: CreateTableModalProps) {
export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTableModalProps) {
const [tableName, setTableName] = useState('');
const [columns, setColumns] = useState<ColumnDefinition[]>([
{ name: 'id', type: 'SERIAL', isPrimary: true, isNullable: false, isUnique: true, defaultValue: '' }
]);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitting] = useState(false);

const addColumn = () => {
setColumns([...columns, {
Expand Down
31 changes: 22 additions & 9 deletions src/components/DataCharts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import html2canvas from 'html2canvas';

// Chart colors matching CSS variables
const CHART_COLORS = [
Expand All @@ -68,7 +67,7 @@ interface FieldAnalysis {
type: 'numeric' | 'categorical' | 'date' | 'unknown';
uniqueValues: number;
hasNulls: boolean;
sample: any;
sample: unknown;
}

interface DataAnalysis {
Expand All @@ -85,13 +84,13 @@ interface DataChartsProps {
result: QueryResult | null;
}

function analyzeField(name: string, values: any[]): FieldAnalysis {
function analyzeField(name: string, values: unknown[]): FieldAnalysis {
const nonNullValues = values.filter(v => v !== null && v !== undefined);
const uniqueValues = new Set(nonNullValues).size;
const sample = nonNullValues[0];

// Check if numeric
const numericCount = nonNullValues.filter(v => typeof v === 'number' || !isNaN(Number(v))).length;
const numericCount = nonNullValues.filter(v => typeof v === 'number' || (typeof v === 'string' && !isNaN(Number(v)))).length;
const isNumeric = numericCount > nonNullValues.length * 0.8;

// Check if date
Expand All @@ -101,7 +100,7 @@ function analyzeField(name: string, values: any[]): FieldAnalysis {
/^\d{2}\.\d{2}\.\d{4}/, // EU date
];
const isDate = nonNullValues.some(v =>
typeof v === 'string' && datePatterns.some(p => p.test(v)) ||
(typeof v === 'string' && datePatterns.some(p => p.test(v))) ||
v instanceof Date
);

Expand Down Expand Up @@ -196,13 +195,23 @@ function formatNumber(value: number): string {
return value.toLocaleString();
}

const CustomTooltip = ({ active, payload, label }: any) => {
interface TooltipProps {
active?: boolean;
payload?: Array<{
name: string;
value: number;
color: string;
}>;
label?: string;
}

const CustomTooltip = ({ active, payload, label }: TooltipProps) => {
if (!active || !payload || !payload.length) return null;

return (
<div className="bg-[#111] border border-white/10 rounded-lg px-3 py-2 shadow-xl">
<p className="text-zinc-400 text-xs mb-1">{label}</p>
{payload.map((entry: any, index: number) => (
{payload.map((entry, index) => (
<p key={index} className="text-sm" style={{ color: entry.color }}>
{entry.name}: <span className="font-mono font-medium">{formatNumber(entry.value)}</span>
</p>
Expand Down Expand Up @@ -239,9 +248,10 @@ export function DataCharts({ result }: DataChartsProps) {
if (!result?.rows || !xAxis) return [];

return result.rows.map(row => {
const dataPoint: any = { [xAxis]: row[xAxis] };
const dataPoint: Record<string, unknown> = { [xAxis]: row[xAxis] };
yAxis.forEach(field => {
dataPoint[field] = Number(row[field]) || 0;
const value = row[field];
dataPoint[field] = typeof value === 'number' ? value : Number(value) || 0;
});
return dataPoint;
});
Expand All @@ -252,6 +262,9 @@ export function DataCharts({ result }: DataChartsProps) {

if (format === 'png') {
try {
// Dynamic import for html2canvas
const html2canvasModule = await import('html2canvas');
const html2canvas = html2canvasModule.default as (element: HTMLElement, options?: { backgroundColor?: string; scale?: number }) => Promise<HTMLCanvasElement>;
const canvas = await html2canvas(chartRef.current, {
backgroundColor: '#080808',
scale: 2,
Expand Down
12 changes: 6 additions & 6 deletions src/components/HealthDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import React, { useEffect, useState } from 'react';
import React, { useEffect, useState, useCallback } from 'react';
import { DatabaseConnection } from '@/lib/types';
import {
Activity,
Expand Down Expand Up @@ -70,7 +70,7 @@ export function HealthDashboard({ connection }: { connection: DatabaseConnection
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

const fetchHealth = async () => {
const fetchHealth = useCallback(async () => {
if (!connection) return;
setLoading(true);
setError(null);
Expand All @@ -83,18 +83,18 @@ export function HealthDashboard({ connection }: { connection: DatabaseConnection
const result = await res.json();
if (result.error) throw new Error(result.error);
setData(result);
} catch (err: any) {
setError(err.message);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error occurred');
} finally {
setLoading(false);
}
};
}, [connection]);

useEffect(() => {
fetchHealth();
const interval = setInterval(fetchHealth, 30000); // Auto refresh every 30s
return () => clearInterval(interval);
}, [connection]);
}, [connection, fetchHealth]);

if (!connection) {
return (
Expand Down
Loading
Loading