diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml
index 2585f3f..e090f47 100644
--- a/.forgejo/workflows/build.yaml
+++ b/.forgejo/workflows/build.yaml
@@ -13,6 +13,9 @@ concurrency:
jobs:
build:
runs-on: docker-ready
+ env:
+ DOCKER_HOST: tcp://127.0.0.1:2375
+ DOCKER_BUILDKIT: "1"
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -22,6 +25,12 @@ jobs:
- name: Set up Docker Buildx
run: |
set -euo pipefail
+ for attempt in $(seq 1 30); do
+ if docker version >/dev/null 2>&1; then
+ break
+ fi
+ sleep 2
+ done
docker version
docker buildx create --use --name nxtgauge-builder || docker buildx use nxtgauge-builder
docker buildx inspect --bootstrap
diff --git a/src/components/AiChatWidget.tsx b/src/components/AiChatWidget.tsx
index 14e1ae8..c50145b 100644
--- a/src/components/AiChatWidget.tsx
+++ b/src/components/AiChatWidget.tsx
@@ -14,6 +14,19 @@ interface ChatResponse {
conversation_id: string;
intent: string;
confidence: number;
+ remaining_credits?: number;
+ remaining_daily_actions?: number;
+ credits_charged?: number;
+}
+
+interface UsageStatus {
+ remaining_credits: number;
+ remaining_daily_actions: number;
+ daily_action_limit: number;
+ plan_code: string;
+ plan_name: string;
+ monthly_credits_total: number;
+ monthly_credits_used: number;
}
export function AiChatWidget() {
@@ -28,6 +41,31 @@ export function AiChatWidget() {
const [input, setInput] = createSignal("");
const [isLoading, setIsLoading] = createSignal(false);
const [conversationId, setConversationId] = createSignal("");
+ const [usage, setUsage] = createSignal(null);
+
+ onMount(() => {
+ fetchUsage();
+ });
+
+ const fetchUsage = async () => {
+ try {
+ const res = await fetch(`${API}/api/ai/usage`);
+ if (!res.ok) return;
+ const data = await res.json();
+ const plan = data.plan || {};
+ setUsage({
+ remaining_credits: plan.remaining_credits ?? 0,
+ remaining_daily_actions: plan.remaining_daily_actions ?? 0,
+ daily_action_limit: plan.daily_action_limit ?? 0,
+ plan_code: plan.plan_code ?? "free",
+ plan_name: plan.plan_name ?? "Free",
+ monthly_credits_total: plan.monthly_credits_total ?? 0,
+ monthly_credits_used: plan.monthly_credits_used ?? 0,
+ });
+ } catch (err) {
+ console.error("Failed to fetch AI usage", err);
+ }
+ };
const toggleChat = () => setIsOpen((v) => !v);
@@ -57,6 +95,19 @@ export function AiChatWidget() {
setConversationId(data.conversation_id);
}
+ // Update usage state from response if available
+ if (typeof data.remaining_credits === "number") {
+ setUsage((prev) =>
+ prev
+ ? {
+ ...prev,
+ remaining_credits: data.remaining_credits!,
+ remaining_daily_actions: data.remaining_daily_actions ?? prev.remaining_daily_actions,
+ }
+ : null
+ );
+ }
+
const assistantMessage: ChatMessage = {
role: "assistant",
content: data.message,
@@ -154,6 +205,13 @@ export function AiChatWidget() {
AI Assistant
+
+ {(u) => (
+
+ {u().plan_name} · {u().remaining_credits} credits · {u().remaining_daily_actions}/{u().daily_action_limit} today
+
+ )}
+
- {formatCurrency(pkg.price)}
+ {formatCurrency(price)}
- +{pkg.tracecoins_amount} Tracecoins
+ +{title}
@@ -399,7 +577,7 @@ export default function CreditsPage(props: Props) {
Payment successful!
- {pkg.tracecoins_amount} Tracecoins have been credited to your wallet.
+ {title} have been credited to your account.
Order ID: {state.orderId}
@@ -623,7 +801,7 @@ export default function CreditsPage(props: Props) {
"font-weight": "700",
}}
>
- Pay {formatCurrency(pkg.price)}
+ Pay {formatCurrency(price)}
Buy Credits
+
+
+
+
+ AI Plan
+
+
+ {aiSummary()?.plan_name ?? "Free"}
+
+
+ {aiSummary()?.remaining_credits ?? 0} credits remaining · {aiSummary()?.remaining_daily_actions ?? 0}/{aiSummary()?.daily_action_limit ?? 0} daily actions
+
+
+
+ {/* Buy AI Credits Tab */}
+
+
+
+ Purchase AI Credits
+
+
+ Buy additional AI credits to power Ask Ash, cover letters, auto-apply, and more.
+
+
+
+
+ No AI credit packages available.
+
+
+
+
0}>
+
+
+ {(pkg) => (
+
+
+ {pkg.name}
+
+
+ {formatCurrency(pkg.price_inr)}
+
+
+ +{pkg.credits} AI Credits
+
+
+
+ {pkg.description}
+
+
+
+
+ )}
+
+
+
+
+
+
+ {/* AI Usage Tab */}
+
+
+
+ AI Usage History
+
+
+ {(s) => (
+
+
+
Plan
+
+ {s().plan_name}
+
+
+
+
Credits Remaining
+
+ {s().remaining_credits}
+
+
+
+
Daily Actions
+
+ {s().remaining_daily_actions}/{s().daily_action_limit}
+
+
+
+
Monthly Used
+
+ {s().monthly_credits_used}/{s().monthly_credits_total}
+
+
+
+ )}
+
+
+
+
+ No AI usage recorded yet.
+
+
+
+
0}>
+
+
+ {(item) => (
+
+
+
+ {item.feature_code}
+
+
+ {item.model_alias} · {formatDate(item.created_at)}
+
+
+
+ -{item.credits_charged} credits
+
+
+ )}
+
+
+
+
+
+
{/* Transactions Tab */}