diff --git a/account.php b/account.php index 3190e2a..00ce7cd 100644 --- a/account.php +++ b/account.php @@ -4,76 +4,666 @@ declare(strict_types=1); require_once __DIR__ . '/includes/bootstrap.php'; require_once __DIR__ . '/includes/FreeTier.php'; require_once __DIR__ . '/includes/PricingCatalog.php'; +require_once __DIR__ . '/includes/CaseStore.php'; +require_once __DIR__ . '/includes/CaseResults.php'; -dbnToolsRequirePageAuth('/dashboard.php#account'); +dbnToolsRequirePageAuth('/account.php'); -header('Location: /dashboard.php#account'); -exit; +$uiLang = dbnToolsCurrentLanguage(); +$langSuffix = $uiLang !== 'en' ? '?lang=' . urlencode($uiLang) : ''; +$isSso = dbnToolsIsFreeTier(); +$userId = $isSso ? (int)($_SESSION['dbn_tools_sso_uid'] ?? 0) : 0; -$uiLang = dbnToolsCurrentLanguage(); $authUser = dbnToolsAuthenticatedUser(); -$isSso = dbnToolsIsFreeTier(); - $email = (string)($authUser['email'] ?? ''); -$role = (string)($authUser['role'] ?? ''); -// Credits & plan (SSO users only) -$detail = $isSso ? FreeTier::balanceDetail((int)$_SESSION['dbn_tools_sso_uid']) : null; -$tier = $detail ? (string)$detail['tier'] : ($isSso ? 'free' : 'caveau'); +$detail = array_merge([ + 'balance' => 0, 'bonus_balance' => 0, 'tier' => $isSso ? 'free' : 'caveau', + 'storage_used_bytes' => 0, 'storage_quota_bytes' => 0, + 'survey_completed_at' => null, 'subscription_period_end' => null, + 'trial_active' => false, 'trial_days_remaining' => 0, 'trial_expires_at' => null, +], $userId > 0 ? (FreeTier::balanceDetail($userId) ?: []) : []); + +$tier = (string)$detail['tier']; +$isPaidTier = in_array($tier, ['plus', 'pro'], true); +$effective = (int)$detail['balance'] + (int)$detail['bonus_balance']; + +$storageBytes = (int)$detail['storage_used_bytes']; +$quotaBytes = (int)$detail['storage_quota_bytes']; +$storageMb = $storageBytes > 0 ? round($storageBytes / 1048576, 1) : 0; +$quotaMb = $quotaBytes > 0 ? (int)round($quotaBytes / 1048576) : 0; +$storagePct = $quotaBytes > 0 ? min(100, (int)round(($storageBytes / $quotaBytes) * 100)) : 0; -$catalogPlans = PricingCatalog::plans(); $tierLabels = [ - 'free' => [$catalogPlans['free']['name'], '#f3f4f6', '#374151'], - 'plus' => [$catalogPlans['plus']['name'], '#ddd6fe', '#5b21b6'], - 'pro' => [$catalogPlans['pro']['name'], '#bfdbfe', '#1e40af'], - 'caveau' => ['CaveauAI', '#d1fae5', '#065f46'], + 'free' => ['Free', '#f3f4f6', '#374151'], + 'plus' => ['Plus', '#ddd6fe', '#5b21b6'], + 'pro' => ['Pro Familie', '#bfdbfe', '#1e40af'], + 'caveau' => ['CaveauAI', '#d1fae5', '#065f46'], ]; $tierLabel = $tierLabels[$tier] ?? $tierLabels['free']; -$monthlyAllowance = $detail ? FreeTier::monthlyAllowance($tier) : 0; -$creditsUsed = $detail ? max(0, $monthlyAllowance - (int)$detail['balance']) : 0; +$profile = $isSso ? dbnToolsMainUserProfile($userId) : null; +$profileVal = static function (string $key) use ($profile): string { + return (string)($profile[$key] ?? ''); +}; -// Team (Caveau sessions only) -$teamMembers = []; -if (!$isSso && !empty($authUser['client_id'])) { +$seatLimit = $tier === 'pro' ? 3 : 1; + +$docs = $isPaidTier ? CaseStore::listDocs($userId) : []; +$results = $isPaidTier ? CaseResults::listForUser($userId, 50) : []; + +$recent = []; +if ($userId > 0) { try { - $db = dbnToolsDb(); - $stmt = $db->prepare( - "SELECT cu.email, cu.role, cu.created_at - FROM client_users cu - WHERE cu.client_id = ? - ORDER BY FIELD(cu.role,'owner','admin','editor','viewer'), cu.created_at ASC" + $stmt = dbnmDb()->prepare( + 'SELECT tool, credits_used, created_at FROM user_tool_usage_log + WHERE user_id = ? ORDER BY created_at DESC LIMIT 25' ); - $stmt->execute([(int)$authUser['client_id']]); - $teamMembers = $stmt->fetchAll(PDO::FETCH_ASSOC); + $stmt->execute([$userId]); + $recent = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (Throwable $e) { - // non-fatal + $recent = []; } } -// Renewal date label -$renewalLabel = ''; -if ($detail) { - if (!empty($detail['trial_active']) && !empty($detail['trial_expires_at'])) { - $renewalLabel = date('d M Y', strtotime((string)$detail['trial_expires_at'])) - . ' (' . (int)$detail['trial_days_remaining'] . ' ' . dbnToolsT('trial_days_left', $uiLang) . ')'; - } elseif (!empty($detail['subscription_period_end'])) { - $renewalLabel = date('d M Y', strtotime((string)$detail['subscription_period_end'])); - } -} +$paidToast = isset($_GET['paid']) && $_GET['paid'] === '1'; + +// ── Localized strings (en / no / uk / pl) ────────────────────────────────── +$L = [ + 'en' => [ + 'page_title' => 'My account', + 'page_lede' => 'Plan, profile, case storage, analyses, usage and MCP access — all in one place.', + 'signed_in_as' => 'Signed in as', + 'jump_plan' => 'Plan', + 'jump_profile' => 'Profile', + 'jump_workspace' => 'Workspace', + 'jump_case' => 'My Case', + 'jump_analyses' => 'Saved analyses', + 'jump_usage' => 'Usage', + 'jump_mcp' => 'MCP', + 'trial_badge' => 'Trial — %d days left', + + 'sec_plan' => 'Plan & credits', + 'current_plan' => 'Current plan', + 'renews_on' => 'Renews on', + 'no_active_sub' => 'No active subscription.', + 'see_plans' => 'See all plans', + 'available_credits' => 'Available credits', + 'monthly' => 'monthly', + 'prepaid' => 'prepaid', + 'manage_subscription'=> 'Manage subscription', + 'upgrade_plan' => 'Upgrade plan', + 'top_up_credits' => 'Top up credits', + 'payment_confirmed' => 'Payment confirmed. If you just subscribed, it may take a few seconds for your account to update.', + 'bonus_survey_title' => 'Bonus credits (survey)', + 'bonus_survey_done' => '✓ You have already received 25 bonus credits for completing the survey.', + 'bonus_survey_pitch' => 'Answer 5 short questions about your needs and receive 25 bonus credits — never expire.', + 'take_survey_btn' => 'Take the survey', + 'portal_loading' => 'Connecting…', + 'portal_error' => 'Could not open the portal.', + 'network_error' => 'Network error: ', + + 'sec_profile' => 'Profile details', + 'profile_optional' => 'Optional', + 'f_display_name' => 'Display name', + 'f_phone' => 'Phone', + 'f_address_1' => 'Address line 1', + 'f_address_2' => 'Address line 2', + 'f_postal' => 'Postal code', + 'f_city' => 'City', + 'f_region' => 'Region', + 'f_country' => 'Country', + 'save_profile' => 'Save profile', + 'profile_saving' => 'Saving…', + 'profile_saved' => 'Saved', + 'profile_error_default' => 'Could not save profile', + + 'sec_workspace' => 'Workspace access', + 'w_email' => 'Email', + 'w_seats' => 'Seats', + 'w_role' => 'Role', + 'w_owner' => 'Owner', + 'w_plan_storage' => 'Plan storage', + 'w_seats_single' => 'Single-user workspace', + 'w_upgrade_for_storage' => 'Upgrade for My Case storage', + + 'sec_case' => 'My Case', + 'case_lede' => 'Upload your case documents once. All tools can reference your private case when you tick "Use my case as context".', + 'case_storage' => 'Storage', + 'case_documents_count' => '%d documents', + 'case_drop_pdf' => 'Drop PDF files here, or click to browse', + 'case_hint' => 'Max 25 MB per file · OCR runs automatically · all stored encrypted in the EU', + 'case_no_docs' => 'No documents yet. Upload your first PDF above.', + 'case_uploaded_ok' => 'Uploaded: %s. OCR starts automatically.', + 'case_over_25mb' => '%s is over 25 MB — split the file first.', + 'case_unknown_err' => 'Unknown error', + 'case_delete_failed' => 'Delete failed.', + 'case_pages' => 'pages', + 'case_uploaded_label' => 'uploaded', + 'case_delete' => 'Delete', + 'case_confirm_delete' => 'Delete this document for good?', + 'case_ocr_pending' => 'Queued', + 'case_ocr_running' => 'OCR in progress', + 'case_ocr_ready' => 'Ready', + 'case_ocr_failed' => 'Failed', + 'case_free_gate_title' => 'My Case — build your own case', + 'case_free_gate_pitch' => 'Upload your case documents once, and let all the tools work on your private corpus.', + 'case_free_gate_b1' => '📄 Private document bank with OCR', + 'case_free_gate_b2' => '🔍 Hybrid search (BM25 + vector) on your case', + 'case_free_gate_b3' => '🧠 All tools can reference your own case', + 'case_free_gate_b4' => '🇪🇺 Everything stored in the EU (Germany/Finland/Norway)', + 'case_free_gate_cta' => 'See plans from NOK 129/mo', + + 'sec_analyses' => 'Saved analyses', + 'analyses_lede' => 'All results from Correspondence, Advocate, CWS, Deep analysis, Discrepancy and Timeline gather here — ready to reopen, rerun or export.', + 'no_analyses' => 'No saved analyses yet. Run a tool to save your first.', + 'used_case_badge' => 'Use my case', + 'pinned_title' => 'Pinned', + 'pin' => 'Pin', + 'unpin' => 'Unpin', + 'open_analysis' => 'Open', + 'delete_analysis' => 'Delete', + 'confirm_delete_analysis' => 'Delete this analysis for good?', + 'pin_failed' => 'Pin failed.', + + 'sec_usage' => 'Recent usage (last 25)', + 'usage_col_tool' => 'Tool / event', + 'usage_col_credits' => 'Credits', + 'usage_col_when' => 'When', + 'no_usage' => 'No usage recorded yet.', + + 'sec_mcp' => 'Developers & MCP', + 'mcp_desc' => 'Connect Claude Desktop, Claude Code, Cursor, or any MCP-compatible client to the full tool suite.', + 'mcp_token_lbl' => 'API token', + 'mcp_no_token' => 'No active token', + 'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)', + 'mcp_remote_lbl' => 'Remote HTTP — Cursor, Zed, Windsurf', + 'mcp_copy' => 'Copy', + 'mcp_manage' => 'Manage tokens →', + 'mcp_full_setup' => 'Full setup guide & token management →', + 'mcp_locked' => 'MCP tokens require Plus or Pro — upgrade to connect AI clients.', + 'mcp_loading' => 'Loading…', + ], + 'no' => [ + 'page_title' => 'Min konto', + 'page_lede' => 'Plan, profil, sak-lagring, analyser, bruk og MCP-tilgang — alt på ett sted.', + 'signed_in_as' => 'Innlogget som', + 'jump_plan' => 'Plan', + 'jump_profile' => 'Profil', + 'jump_workspace' => 'Arbeidsområde', + 'jump_case' => 'Min Sak', + 'jump_analyses' => 'Lagrede analyser', + 'jump_usage' => 'Bruk', + 'jump_mcp' => 'MCP', + 'trial_badge' => 'Prøveperiode — %d dager igjen', + + 'sec_plan' => 'Plan & kreditter', + 'current_plan' => 'Nåværende plan', + 'renews_on' => 'Fornyes', + 'no_active_sub' => 'Ingen aktiv abonnement.', + 'see_plans' => 'Se alle planer', + 'available_credits' => 'Tilgjengelige kreditter', + 'monthly' => 'månedlige', + 'prepaid' => 'forhåndsbetalte', + 'manage_subscription'=> 'Administrer abonnement', + 'upgrade_plan' => 'Oppgrader plan', + 'top_up_credits' => 'Kjøp kreditter', + 'payment_confirmed' => 'Betalingen er bekreftet. Hvis du nettopp abonnerte, kan det ta noen sekunder før kontoen oppdateres.', + 'bonus_survey_title' => 'Bonus-kreditter (undersøkelse)', + 'bonus_survey_done' => '✓ Du har allerede mottatt 25 bonuskreditter for å fylle ut undersøkelsen.', + 'bonus_survey_pitch' => 'Svar på 5 korte spørsmål om dine behov og motta 25 bonus-kreditter — utløper aldri.', + 'take_survey_btn' => 'Ta undersøkelsen', + 'portal_loading' => 'Kobler til…', + 'portal_error' => 'Kunne ikke åpne portalen.', + 'network_error' => 'Nettverksfeil: ', + + 'sec_profile' => 'Profildetaljer', + 'profile_optional' => 'Valgfritt', + 'f_display_name' => 'Visningsnavn', + 'f_phone' => 'Telefon', + 'f_address_1' => 'Adresselinje 1', + 'f_address_2' => 'Adresselinje 2', + 'f_postal' => 'Postnummer', + 'f_city' => 'Sted', + 'f_region' => 'Fylke', + 'f_country' => 'Land', + 'save_profile' => 'Lagre profil', + 'profile_saving' => 'Lagrer…', + 'profile_saved' => 'Lagret', + 'profile_error_default' => 'Kunne ikke lagre profilen', + + 'sec_workspace' => 'Arbeidsområde-tilgang', + 'w_email' => 'E-post', + 'w_seats' => 'Plasser', + 'w_role' => 'Rolle', + 'w_owner' => 'Eier', + 'w_plan_storage' => 'Plan-lagring', + 'w_seats_single' => 'Enkeltbruker-arbeidsområde', + 'w_upgrade_for_storage' => 'Oppgrader for Min Sak-lagring', + + 'sec_case' => 'Min Sak', + 'case_lede' => 'Last opp dokumentene dine én gang. Alle verktøyene kan deretter referere til din private sak når du krysser av "Bruk min sak som kontekst".', + 'case_storage' => 'Lagring', + 'case_documents_count' => '%d dokumenter', + 'case_drop_pdf' => 'Slipp PDF-filer her, eller klikk for å bla', + 'case_hint' => 'Maks 25 MB per fil · OCR kjøres automatisk · alt lagres kryptert i EU', + 'case_no_docs' => 'Ingen dokumenter ennå. Last opp din første PDF over.', + 'case_uploaded_ok' => 'Lastet opp: %s. OCR starter automatisk.', + 'case_over_25mb' => '%s er over 25 MB — del opp filen først.', + 'case_unknown_err' => 'Ukjent feil', + 'case_delete_failed' => 'Sletting feilet.', + 'case_pages' => 'sider', + 'case_uploaded_label' => 'lastet opp', + 'case_delete' => 'Slett', + 'case_confirm_delete' => 'Slette dette dokumentet for godt?', + 'case_ocr_pending' => 'I kø', + 'case_ocr_running' => 'OCR pågår', + 'case_ocr_ready' => 'Klar', + 'case_ocr_failed' => 'Feilet', + 'case_free_gate_title' => 'Min Sak — bygg din egen sak', + 'case_free_gate_pitch' => 'Last opp dokumentene fra saken din én gang, og la alle verktøyene jobbe på din private korpus.', + 'case_free_gate_b1' => '📄 Privat dokumentbank med OCR', + 'case_free_gate_b2' => '🔍 Hybrid søk (BM25 + vektor) i din sak', + 'case_free_gate_b3' => '🧠 Alle verktøy kan referere til din egen sak', + 'case_free_gate_b4' => '🇪🇺 Alt lagres i EU (Tyskland/Finland/Norge)', + 'case_free_gate_cta' => 'Se planer fra NOK 129/mo', + + 'sec_analyses' => 'Lagrede analyser', + 'analyses_lede' => 'Alle resultater fra Korrespondanse, Advokat, BVJ, Dyp analyse, Motstrid og Tidslinje samles her — klar til å gjenåpnes, kjøres på nytt eller eksporteres.', + 'no_analyses' => 'Ingen lagrede analyser ennå. Kjør et verktøy for å lagre din første.', + 'used_case_badge' => 'Bruk min sak', + 'pinned_title' => 'Festet', + 'pin' => 'Fest', + 'unpin' => 'Løsne', + 'open_analysis' => 'Åpne', + 'delete_analysis' => 'Slett', + 'confirm_delete_analysis' => 'Slette denne analysen for godt?', + 'pin_failed' => 'Festing feilet.', + + 'sec_usage' => 'Nylig bruk (siste 25)', + 'usage_col_tool' => 'Verktøy / hendelse', + 'usage_col_credits' => 'Kreditter', + 'usage_col_when' => 'Tidspunkt', + 'no_usage' => 'Ingen bruk registrert ennå.', + + 'sec_mcp' => 'Utviklere & MCP', + 'mcp_desc' => 'Koble Claude Desktop, Claude Code, Cursor eller annen MCP-klient til hele verktøysuiten.', + 'mcp_token_lbl' => 'API-token', + 'mcp_no_token' => 'Ingen aktiv token', + 'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)', + 'mcp_remote_lbl' => 'Ekstern HTTP — Cursor, Zed, Windsurf', + 'mcp_copy' => 'Kopier', + 'mcp_manage' => 'Administrer tokens →', + 'mcp_full_setup' => 'Full oppsettsguide & token-administrasjon →', + 'mcp_locked' => 'MCP-tokens krever Plus eller Pro — oppgrader for å koble til AI-klienter.', + 'mcp_loading' => 'Laster…', + ], + 'uk' => [ + 'page_title' => 'Мій обліковий запис', + 'page_lede' => 'План, профіль, сховище справ, аналізи, використання та доступ MCP — все в одному місці.', + 'signed_in_as' => 'Ввійшли як', + 'jump_plan' => 'План', + 'jump_profile' => 'Профіль', + 'jump_workspace' => 'Робочий простір', + 'jump_case' => 'Моя справа', + 'jump_analyses' => 'Збережені аналізи', + 'jump_usage' => 'Використання', + 'jump_mcp' => 'MCP', + 'trial_badge' => 'Пробний — %d дн. залишилось', + + 'sec_plan' => 'План і кредити', + 'current_plan' => 'Поточний план', + 'renews_on' => 'Поновлюється', + 'no_active_sub' => 'Немає активної підписки.', + 'see_plans' => 'Переглянути всі плани', + 'available_credits' => 'Доступні кредити', + 'monthly' => 'місячних', + 'prepaid' => 'передоплачених', + 'manage_subscription'=> 'Керувати підпискою', + 'upgrade_plan' => 'Покращити план', + 'top_up_credits' => 'Поповнити кредити', + 'payment_confirmed' => 'Платіж підтверджено. Якщо ви щойно підписалися, оновлення облікового запису може зайняти кілька секунд.', + 'bonus_survey_title' => 'Бонусні кредити (опитування)', + 'bonus_survey_done' => '✓ Ви вже отримали 25 бонусних кредитів за заповнення опитування.', + 'bonus_survey_pitch' => 'Дайте відповідь на 5 коротких запитань про ваші потреби та отримайте 25 бонусних кредитів — без терміну дії.', + 'take_survey_btn' => 'Пройти опитування', + 'portal_loading' => 'З\'єднання…', + 'portal_error' => 'Не вдалося відкрити портал.', + 'network_error' => 'Помилка мережі: ', + + 'sec_profile' => 'Дані профілю', + 'profile_optional' => 'Необов\'язково', + 'f_display_name' => 'Ім\'я для показу', + 'f_phone' => 'Телефон', + 'f_address_1' => 'Адреса, рядок 1', + 'f_address_2' => 'Адреса, рядок 2', + 'f_postal' => 'Поштовий індекс', + 'f_city' => 'Місто', + 'f_region' => 'Регіон', + 'f_country' => 'Країна', + 'save_profile' => 'Зберегти профіль', + 'profile_saving' => 'Збереження…', + 'profile_saved' => 'Збережено', + 'profile_error_default' => 'Не вдалося зберегти профіль', + + 'sec_workspace' => 'Доступ до робочого простору', + 'w_email' => 'Email', + 'w_seats' => 'Місця', + 'w_role' => 'Роль', + 'w_owner' => 'Власник', + 'w_plan_storage' => 'Сховище плану', + 'w_seats_single' => 'Робочий простір на одного користувача', + 'w_upgrade_for_storage' => 'Оновіть для сховища «Моя справа»', + + 'sec_case' => 'Моя справа', + 'case_lede' => 'Завантажте документи вашої справи один раз. Усі інструменти зможуть посилатися на вашу приватну справу, коли ви поставите галочку «Використати мою справу як контекст».', + 'case_storage' => 'Сховище', + 'case_documents_count' => '%d документів', + 'case_drop_pdf' => 'Перетягніть PDF-файли сюди або клацніть, щоб вибрати', + 'case_hint' => 'Макс. 25 МБ на файл · OCR запускається автоматично · все зберігається зашифрованим у ЄС', + 'case_no_docs' => 'Поки що немає документів. Завантажте перший PDF вище.', + 'case_uploaded_ok' => 'Завантажено: %s. OCR починається автоматично.', + 'case_over_25mb' => '%s перевищує 25 МБ — розділіть файл спочатку.', + 'case_unknown_err' => 'Невідома помилка', + 'case_delete_failed' => 'Видалення не вдалося.', + 'case_pages' => 'стор.', + 'case_uploaded_label' => 'завантажено', + 'case_delete' => 'Видалити', + 'case_confirm_delete' => 'Видалити цей документ назавжди?', + 'case_ocr_pending' => 'У черзі', + 'case_ocr_running' => 'OCR виконується', + 'case_ocr_ready' => 'Готово', + 'case_ocr_failed' => 'Не вдалося', + 'case_free_gate_title' => 'Моя справа — побудуйте власну справу', + 'case_free_gate_pitch' => 'Завантажте документи з вашої справи один раз, і нехай усі інструменти працюють з вашим приватним корпусом.', + 'case_free_gate_b1' => '📄 Приватний банк документів з OCR', + 'case_free_gate_b2' => '🔍 Гібридний пошук (BM25 + вектор) у вашій справі', + 'case_free_gate_b3' => '🧠 Усі інструменти можуть посилатися на вашу справу', + 'case_free_gate_b4' => '🇪🇺 Усе зберігається в ЄС (Німеччина/Фінляндія/Норвегія)', + 'case_free_gate_cta' => 'Переглянути плани від NOK 129/міс', + + 'sec_analyses' => 'Збережені аналізи', + 'analyses_lede' => 'Усі результати з Кореспонденції, Адвоката, BVJ, Глибокого аналізу, Розбіжностей і Хронології збираються тут — готові до повторного відкриття, перезапуску чи експорту.', + 'no_analyses' => 'Поки що немає збережених аналізів. Запустіть інструмент, щоб зберегти перший.', + 'used_case_badge' => 'Моя справа', + 'pinned_title' => 'Закріплено', + 'pin' => 'Закріпити', + 'unpin' => 'Відкріпити', + 'open_analysis' => 'Відкрити', + 'delete_analysis' => 'Видалити', + 'confirm_delete_analysis' => 'Видалити цей аналіз назавжди?', + 'pin_failed' => 'Закріпити не вдалося.', + + 'sec_usage' => 'Нещодавнє використання (останні 25)', + 'usage_col_tool' => 'Інструмент / подія', + 'usage_col_credits' => 'Кредити', + 'usage_col_when' => 'Коли', + 'no_usage' => 'Використання ще не зареєстровано.', + + 'sec_mcp' => 'Розробники та MCP', + 'mcp_desc' => 'Підключайте Claude Desktop, Claude Code, Cursor або будь-який MCP-клієнт до повного набору інструментів.', + 'mcp_token_lbl' => 'API-токен', + 'mcp_no_token' => 'Немає активного токена', + 'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)', + 'mcp_remote_lbl' => 'Віддалений HTTP — Cursor, Zed, Windsurf', + 'mcp_copy' => 'Копіювати', + 'mcp_manage' => 'Керувати токенами →', + 'mcp_full_setup' => 'Повна документація та управління токенами →', + 'mcp_locked' => 'MCP-токени потребують Plus або Pro — оновіться для підключення AI-клієнтів.', + 'mcp_loading' => 'Завантаження…', + ], + 'pl' => [ + 'page_title' => 'Moje konto', + 'page_lede' => 'Plan, profil, magazyn sprawy, analizy, użycie i dostęp MCP — wszystko w jednym miejscu.', + 'signed_in_as' => 'Zalogowany jako', + 'jump_plan' => 'Plan', + 'jump_profile' => 'Profil', + 'jump_workspace' => 'Obszar roboczy', + 'jump_case' => 'Moja sprawa', + 'jump_analyses' => 'Zapisane analizy', + 'jump_usage' => 'Użycie', + 'jump_mcp' => 'MCP', + 'trial_badge' => 'Próba — %d dni pozostało', + + 'sec_plan' => 'Plan i kredyty', + 'current_plan' => 'Bieżący plan', + 'renews_on' => 'Odnawia się', + 'no_active_sub' => 'Brak aktywnej subskrypcji.', + 'see_plans' => 'Zobacz wszystkie plany', + 'available_credits' => 'Dostępne kredyty', + 'monthly' => 'miesięcznych', + 'prepaid' => 'przedpłaconych', + 'manage_subscription'=> 'Zarządzaj subskrypcją', + 'upgrade_plan' => 'Ulepsz plan', + 'top_up_credits' => 'Doładuj kredyty', + 'payment_confirmed' => 'Płatność potwierdzona. Jeśli właśnie wykupiono subskrypcję, aktualizacja konta może potrwać kilka sekund.', + 'bonus_survey_title' => 'Kredyty bonusowe (ankieta)', + 'bonus_survey_done' => '✓ Otrzymałeś już 25 kredytów bonusowych za wypełnienie ankiety.', + 'bonus_survey_pitch' => 'Odpowiedz na 5 krótkich pytań o swoje potrzeby i otrzymaj 25 kredytów bonusowych — nigdy nie wygasają.', + 'take_survey_btn' => 'Wypełnij ankietę', + 'portal_loading' => 'Łączenie…', + 'portal_error' => 'Nie można otworzyć portalu.', + 'network_error' => 'Błąd sieci: ', + + 'sec_profile' => 'Dane profilu', + 'profile_optional' => 'Opcjonalne', + 'f_display_name' => 'Nazwa wyświetlana', + 'f_phone' => 'Telefon', + 'f_address_1' => 'Adres linia 1', + 'f_address_2' => 'Adres linia 2', + 'f_postal' => 'Kod pocztowy', + 'f_city' => 'Miasto', + 'f_region' => 'Region', + 'f_country' => 'Kraj', + 'save_profile' => 'Zapisz profil', + 'profile_saving' => 'Zapisywanie…', + 'profile_saved' => 'Zapisano', + 'profile_error_default' => 'Nie udało się zapisać profilu', + + 'sec_workspace' => 'Dostęp do obszaru roboczego', + 'w_email' => 'Email', + 'w_seats' => 'Miejsca', + 'w_role' => 'Rola', + 'w_owner' => 'Właściciel', + 'w_plan_storage' => 'Magazyn planu', + 'w_seats_single' => 'Obszar roboczy jednoosobowy', + 'w_upgrade_for_storage' => 'Ulepsz, aby uzyskać magazyn Mojej sprawy', + + 'sec_case' => 'Moja sprawa', + 'case_lede' => 'Prześlij dokumenty swojej sprawy raz. Wszystkie narzędzia mogą się do nich odwoływać, gdy zaznaczysz „Użyj mojej sprawy jako kontekstu".', + 'case_storage' => 'Magazyn', + 'case_documents_count' => '%d dokumentów', + 'case_drop_pdf' => 'Upuść pliki PDF tutaj lub kliknij, aby przeglądać', + 'case_hint' => 'Maks. 25 MB na plik · OCR działa automatycznie · wszystko przechowywane zaszyfrowane w UE', + 'case_no_docs' => 'Brak dokumentów. Prześlij swój pierwszy PDF powyżej.', + 'case_uploaded_ok' => 'Przesłano: %s. OCR rozpoczyna się automatycznie.', + 'case_over_25mb' => '%s przekracza 25 MB — najpierw podziel plik.', + 'case_unknown_err' => 'Nieznany błąd', + 'case_delete_failed' => 'Usuwanie nie powiodło się.', + 'case_pages' => 'stron', + 'case_uploaded_label' => 'przesłano', + 'case_delete' => 'Usuń', + 'case_confirm_delete' => 'Usunąć ten dokument na zawsze?', + 'case_ocr_pending' => 'W kolejce', + 'case_ocr_running' => 'OCR w toku', + 'case_ocr_ready' => 'Gotowe', + 'case_ocr_failed' => 'Nieudane', + 'case_free_gate_title' => 'Moja sprawa — zbuduj własną sprawę', + 'case_free_gate_pitch' => 'Prześlij dokumenty swojej sprawy raz i pozwól wszystkim narzędziom pracować na Twoim prywatnym korpusie.', + 'case_free_gate_b1' => '📄 Prywatny bank dokumentów z OCR', + 'case_free_gate_b2' => '🔍 Wyszukiwanie hybrydowe (BM25 + wektor) w Twojej sprawie', + 'case_free_gate_b3' => '🧠 Wszystkie narzędzia mogą odwoływać się do Twojej sprawy', + 'case_free_gate_b4' => '🇪🇺 Wszystko przechowywane w UE (Niemcy/Finlandia/Norwegia)', + 'case_free_gate_cta' => 'Zobacz plany od NOK 129/mies', + + 'sec_analyses' => 'Zapisane analizy', + 'analyses_lede' => 'Wszystkie wyniki z Korespondencji, Adwokata, BVJ, Głębokiej analizy, Rozbieżności i Osi czasu zbierają się tutaj — gotowe do ponownego otwarcia, uruchomienia lub eksportu.', + 'no_analyses' => 'Brak zapisanych analiz. Uruchom narzędzie, aby zapisać pierwszą.', + 'used_case_badge' => 'Moja sprawa', + 'pinned_title' => 'Przypięte', + 'pin' => 'Przypnij', + 'unpin' => 'Odepnij', + 'open_analysis' => 'Otwórz', + 'delete_analysis' => 'Usuń', + 'confirm_delete_analysis' => 'Usunąć tę analizę na zawsze?', + 'pin_failed' => 'Przypinanie nie powiodło się.', + + 'sec_usage' => 'Ostatnie użycie (ostatnie 25)', + 'usage_col_tool' => 'Narzędzie / zdarzenie', + 'usage_col_credits' => 'Kredyty', + 'usage_col_when' => 'Kiedy', + 'no_usage' => 'Nie zarejestrowano jeszcze użycia.', + + 'sec_mcp' => 'Deweloperzy i MCP', + 'mcp_desc' => 'Podłącz Claude Desktop, Claude Code, Cursor lub dowolnego klienta MCP do pełnego zestawu narzędzi.', + 'mcp_token_lbl' => 'Token API', + 'mcp_no_token' => 'Brak aktywnego tokenu', + 'mcp_stdio_lbl' => 'Claude Desktop / Claude Code (stdio)', + 'mcp_remote_lbl' => 'Zdalny HTTP — Cursor, Zed, Windsurf', + 'mcp_copy' => 'Kopiuj', + 'mcp_manage' => 'Zarządzaj tokenami →', + 'mcp_full_setup' => 'Pełna dokumentacja i zarządzanie tokenami →', + 'mcp_locked' => 'Tokeny MCP wymagają Plus lub Pro — zaktualizuj, aby połączyć klientów AI.', + 'mcp_loading' => 'Ładowanie…', + ], +]; +$l = $L[$uiLang] ?? $L['en']; + +$seatsLabel = $seatLimit > 1 ? '1 / ' . $seatLimit : $l['w_seats_single']; + +$dateLocale = match ($uiLang) { + 'no' => 'j. F Y', + default => 'j M Y', +}; ?> - <?= htmlspecialchars(dbnToolsT('account_title', $uiLang)) ?> — Do Better Norge + <?= htmlspecialchars($l['page_title']) ?> — Do Better Norge + + + diff --git a/advocate.php b/advocate.php index ae59b27..4049f9c 100644 --- a/advocate.php +++ b/advocate.php @@ -56,8 +56,9 @@ require_once __DIR__ . '/includes/layout.php'; + -

Azure mini finishes fastest. Azure full produces the most thorough advocate brief. Norwegian specialist v3 is a Qwen2.5 fine-tune trained on barnevernsloven, ECHR, and forvaltningsloven — highest precision for § 4-25, Strand Lobben, and procedural red flags.

+

Azure mini finishes fastest. Claude Sonnet 4.6 via AWS Bedrock produces the most thorough advocate brief — superior at multi-party legal reasoning, ECHR precedent weighting, and long-form argumentation. Norwegian specialist v3 is a Qwen2.5 fine-tune trained on barnevernsloven, ECHR, and forvaltningsloven — highest precision for § 4-25, Strand Lobben, and procedural red flags.

Corpus slices

diff --git a/billing.php b/billing.php index 952c46c..d500a23 100644 --- a/billing.php +++ b/billing.php @@ -1,205 +1,9 @@ 0 ? FreeTier::balanceDetail($userId) : [ - 'balance' => 0, 'bonus_balance' => 0, 'tier' => 'caveau', - 'storage_used_bytes' => 0, 'storage_quota_bytes' => 0, - 'survey_completed_at' => null, 'subscription_period_end' => null, - 'trial_active' => false, 'trial_days_remaining' => 0, 'trial_expires_at' => null, -]; - -$tier = (string)$detail['tier']; -$isPaidTier = in_array($tier, ['plus', 'pro'], true); -$effective = (int)$detail['balance'] + (int)$detail['bonus_balance']; -$storageMb = round($detail['storage_used_bytes'] / 1048576, 1); -$quotaMb = $detail['storage_quota_bytes'] > 0 ? round($detail['storage_quota_bytes'] / 1048576, 0) : 0; -$storagePct = $quotaMb > 0 ? min(100, round(($storageMb / $quotaMb) * 100)) : 0; - -$tierLabels = array_map(static fn(array $plan): string => (string)$plan['name'], PricingCatalog::plans()); -$tierLabels['caveau'] = 'CaveauAI'; - -// Recent usage -$db = dbnmDb(); -$stmt = $db->prepare( - 'SELECT tool, credits_used, created_at FROM user_tool_usage_log - WHERE user_id = ? ORDER BY created_at DESC LIMIT 25' -); -$stmt->execute([$userId]); -$recent = $stmt->fetchAll(PDO::FETCH_ASSOC); - -$status = (string)($_GET['status'] ?? ''); -?> - - - - - - Konto & fakturering — tools.dobetternorge.no - - - - - - - - - -
-

Konto & fakturering

-

· Tilbake til dashbordet

- - -

Betalingen er bekreftet. Hvis du nettopp abonnerte, kan det ta noen sekunder før kontoen oppdateres.

- - -
-
-

Nåværende plan

-

- -

- -

Plus prøveperiode: dager igjen. Kortet belastes automatisk etter prøveperioden hvis du ikke kansellerer.

- - -

Fornyes

- -

Ingen aktiv abonnement. Se planer

- -
- - - - Se alle planer -
-
- -
-

Tilgjengelige kreditter

-

-

- månedlige · forhåndsbetalte -

-
- - -
-

Sak-lagring

-

MB / MB

-
-

Gå til Min Sak

-
- -
-

Min Sak

-

Last opp dine egne dokumenter — alle verktøyene kan deretter referere til din private sak.

- Oppgrader for å bygge sak -
- - -
-

Bonus-kreditter (undersøkelse)

- -

✓ Du har allerede mottatt 25 bonuskreditter for å fylle ut undersøkelsen.

- -

Svar på 5 korte spørsmål om dine behov og motta 25 bonus-kreditter — utløper aldri.

- Ta undersøkelsen - -
-
- -
-

Nylig bruk (siste 25)

- -

Ingen bruk registrert ennå.

- - - - - - - - - - - - - -
Verktøy / hendelseKreditterTidspunkt
- -
-
- - - - +$paid = (isset($_GET['status']) && $_GET['status'] === 'success') ? '?paid=1' : ''; +header('Location: /account.php' . $paid . '#plan', true, 302); +exit; diff --git a/dashboard.php b/dashboard.php index 121e191..1ec00b8 100644 --- a/dashboard.php +++ b/dashboard.php @@ -289,7 +289,7 @@ window.DBN_TOOLS_LANG = ;
- + @@ -302,10 +302,10 @@ window.DBN_TOOLS_LANG = ;

-

· ·

+

· ·

- +

; -
diff --git a/includes/DbnBedrockGateway.php b/includes/DbnBedrockGateway.php index c52d876..846521c 100644 --- a/includes/DbnBedrockGateway.php +++ b/includes/DbnBedrockGateway.php @@ -96,9 +96,10 @@ final class DbnBedrockGateway 'temperature' => (float)($options['temperature'] ?? 0.2), 'max_tokens' => $options['max_tokens'] ?? 1200, ]; - if (!empty($options['json'])) { - $payload['response_format'] = ['type' => 'json_object']; - } + // response_format is intentionally omitted for Claude via Bedrock. + // LiteLLM converts json_object to a tool-use constraint, routing output + // into tool_calls instead of content. Claude follows JSON instructions + // in the system prompt without needing response_format. return $this->postJson($this->liteLlmUrl, $payload, (int)($options['timeout'] ?? 90)); } diff --git a/includes/DeepResearchAgent.php b/includes/DeepResearchAgent.php index 1638c12..e5db729 100644 --- a/includes/DeepResearchAgent.php +++ b/includes/DeepResearchAgent.php @@ -40,7 +40,7 @@ final class DbnDeepResearchAgent ): array { $seedQuery = trim($seedQuery); $pastedText = trim($pastedText); - $engine = in_array($engine, ['azure_mini', 'azure_full', 'gpu'], true) ? $engine : 'azure_mini'; + $engine = in_array($engine, ['azure_mini', 'azure_full', 'gpu', 'dbn_legal', 'dbn_legal_v3', 'claude_sonnet', 'claude_haiku'], true) ? $engine : 'azure_mini'; $language = dbnToolsNormalizeUiLanguage($language); $controls = $this->normalizeControls($controls); @@ -1221,7 +1221,7 @@ PROMPT; ): array { $seedQuery = trim($seedQuery); $pastedText = trim($pastedText); - $engine = in_array($engine, ['azure_mini', 'azure_full', 'gpu'], true) ? $engine : 'azure_mini'; + $engine = in_array($engine, ['azure_mini', 'azure_full', 'gpu', 'dbn_legal', 'dbn_legal_v3', 'claude_sonnet', 'claude_haiku'], true) ? $engine : 'azure_mini'; $language = dbnToolsNormalizeUiLanguage($language); $controls = $this->normalizeControls($controls); diff --git a/includes/nav.php b/includes/nav.php index e43666e..69a40a4 100644 --- a/includes/nav.php +++ b/includes/nav.php @@ -88,7 +88,7 @@ $_navAssetBase = str_contains($_navScriptPath, '/dashboard/') ? '../assets' : 'a
- + diff --git a/min-sak.php b/min-sak.php index 3040d94..3ee4744 100644 --- a/min-sak.php +++ b/min-sak.php @@ -1,321 +1,8 @@ - - Min Sak — Do Better Norge - - - -

Se planer fra NOK 129/mo

-
0 ? min(100, round(($used / $quota) * 100)) : 0; -?> - - - - - - Min Sak — tools.dobetternorge.no - - - - - - - - - -
- - -
-
-

- ← Dashbord · Fakturering -

-

Min Sak

-

Last opp dokumentene dine én gang. Alle verktøyene kan deretter referere til din private sak når du krysser av "Bruk min sak som kontekst".

- -
-
-

Lagring

-
MB / MB
-
-

dokumenter

-
-
-

Plan

-
- 'Plus','pro'=>'Pro Familie'][$tier] ?? ucfirst($tier)) ?> - - - Prøveperiode · dager igjen - - -
-

- Oppgrader · Administrer -

-
-
- - - -
- -
- -

Ingen dokumenter ennå. Last opp din første PDF over.

- -
-
📄
-
-
-
- KB - · sider - · - · lastet opp -
-
- - 'I kø','running'=>'OCR pågår','ready'=>'Klar','failed'=>'Feilet'][$d['ocr_status']] ?? $d['ocr_status']); - ?> - -
- -
-
- -
- - -

Lagrede analyser

-

Alle resultater fra Korrespondanse, Advokat, BVJ, Dyp analyse, Motstrid og Tidslinje samles her — klar til å gjenåpnes, kjøres på nytt eller eksporteres.

-
- -

Ingen lagrede analyser ennå. Kjør et verktøy for å lagre din første.

- -
-
-
-
- - - - - - -
-
- - · - - · Bruk min sak - -
-
-
- - - Åpne - - -
-
- -
-
- - - - +header('Location: /account.php#case', true, 302); +exit;