Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions client/apps/landing-page/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/mdx": "^4.3.1",
"@hatchgrid/logger": "workspace:*",
"@astrojs/rss": "^4.0.12",
"@astrojs/sitemap": "^3.4.1",
"@astrojs/vue": "^5.1.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { LogManager } from "@hatchgrid/logger";
import { toTypedSchema } from "@vee-validate/zod";
import { useForm } from "vee-validate";
import type { HTMLAttributes } from "vue";
Expand Down Expand Up @@ -57,6 +58,7 @@ const props = withDefaults(defineProps<Props>(), {
lang: "en",
});

const logger = LogManager.getLogger("landing-page:cta-email");
const t = useTranslations(props.lang);
// Initialize composables
const { validationSchema } = useEmailValidation({ lang: props.lang });
Expand Down Expand Up @@ -110,7 +112,7 @@ const onSubmit = handleSubmit(async (values) => {
resetForm();

// Emit success event for parent components
console.log("✅ Email successfully submitted:", {
logger.info("Email successfully submitted", {
email: values.email,
response: result.data,
});
Expand All @@ -120,7 +122,7 @@ const onSubmit = handleSubmit(async (values) => {
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Unknown error";
showErrorToast(errorMessage);
console.error("Email submission failed:", err);
logger.error("Email submission failed", { error: err });
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { LogManager } from "@hatchgrid/logger";
import { toTypedSchema } from "@vee-validate/zod";
import { useForm } from "vee-validate";
import type { HTMLAttributes } from "vue";
Expand Down Expand Up @@ -47,6 +48,8 @@ const props = withDefaults(defineProps<Props>(), {
buttonSize: "default",
});

const logger = LogManager.getLogger("landing-page:ui-cta-email");

const formSchema = toTypedSchema(
z.object({
email: z
Expand All @@ -69,7 +72,7 @@ const onSubmit = handleSubmit((values) => {
),
});
// Here you would typically send the email to your backend
console.log("Email submitted:", values.email);
logger.info("Email submitted", { email: values.email });
});
</script>

Expand Down
6 changes: 4 additions & 2 deletions client/apps/landing-page/src/i18n/ui.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { LogManager } from "@hatchgrid/logger";
import type { Lang, UIDict, UIMultilingual } from "./types";

// Use import.meta.glob to get all translation files
const logger = LogManager.getLogger("landing-page:i18n-ui");
const translationModules = import.meta.glob<
Record<string, Record<Lang, UIDict>>
>("./translations/*.ts", { eager: true });
Expand Down Expand Up @@ -38,8 +40,8 @@ export const ui: UIMultilingual = Object.values(translationModules).reduce(

// Enhanced debug log to check what was loaded
if (process.env.NODE_ENV === "development") {
console.log(
"㊙︎ Loaded translations:",
logger.debug(
"Loaded translations",
Object.keys(ui)
.map((lang) => `${lang}: ${Object.keys(ui[lang as Lang]).length} keys`)
.join(", "),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
*/

import { getCollection, getEntry } from "astro:content";
import { LogManager } from "@hatchgrid/logger";
import { parseEntityId } from "@/utils/collection.entity";
import type { CategoryCriteria } from "./category.criteria";
import { toCategories, toCategory } from "./category.mapper";
import type Category from "./category.model";

const logger = LogManager.getLogger("landing-page:category-service");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Minor formatting nitpick

There's an extra blank line that could be removed for cleaner code formatting.

const logger = LogManager.getLogger("landing-page:category-service");
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const logger = LogManager.getLogger("landing-page:category-service");
🤖 Prompt for AI Agents
In client/apps/landing-page/src/models/category/category.service.ts at line 14,
remove the extra blank line to improve code formatting and maintain consistency.

/**
* Retrieves categories from the content collection with filtering options
* @async
Expand Down Expand Up @@ -70,7 +73,7 @@ export const getCategoryById = async (
const entry = await getEntry("categories", categoryId);
return entry ? toCategory(entry) : undefined;
} catch (error) {
console.error(`Failed to fetch category ${categoryId}:`, error);
logger.error(`Failed to fetch category ${categoryId}`, { error });
throw new Error(`Failed to fetch category ${categoryId}`);
}
};
6 changes: 4 additions & 2 deletions client/apps/landing-page/src/models/tag/tag.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
*/

import { getCollection, getEntry } from "astro:content";
import { LogManager } from "@hatchgrid/logger";
import { parseEntityId } from "@/utils/collection.entity";
import type { TagCriteria } from "./tag.criteria";
import { toTag, toTags } from "./tag.mapper";
import type Tag from "./tag.model";

const logger = LogManager.getLogger("landing-page:tag-service");
const tagsCache: Record<string, Tag[]> = {};

/**
Expand Down Expand Up @@ -58,7 +60,7 @@ export async function getTags(criteria?: TagCriteria): Promise<Tag[]> {
tagsCache[cacheKey] = mappedTags;
return mappedTags;
} catch (error) {
console.error("Failed to fetch tags:", error);
logger.error("Failed to fetch tags", { error });
throw new Error("Failed to fetch tags");
}
}
Expand All @@ -74,7 +76,7 @@ export async function getTagById(id: string): Promise<Tag | undefined> {
const entry = await getEntry("tags", id);
return entry ? toTag(entry) : undefined;
} catch (error) {
console.error(`Failed to fetch tag with id ${id}:`, error);
logger.error(`Failed to fetch tag with id ${id}`, { error });
throw new Error(`Failed to fetch tag with id ${id}`);
}
}
5 changes: 4 additions & 1 deletion client/apps/landing-page/src/utils/test/mockEmailApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* This simulates the backend API responses for email submissions
*/

import { LogManager } from "@hatchgrid/logger";

export interface MockApiConfig {
delay?: number;
successRate?: number;
Expand Down Expand Up @@ -94,6 +96,7 @@ export class MockEmailApi {
}

// Global mock setup for fetch in development
const logger = LogManager.getLogger("landing-page:mock-email-api");
export function setupMockApi(config?: MockApiConfig) {
if (typeof window !== "undefined" && process.env.NODE_ENV === "development") {
const mockApi = new MockEmailApi(config);
Expand Down Expand Up @@ -132,7 +135,7 @@ export function setupMockApi(config?: MockApiConfig) {
return originalFetch(url, options);
};

console.log("🚀 Mock Email API enabled for development");
logger.info("Mock Email API enabled for development");
// return mockApi // This return is not strictly necessary if not used elsewhere after setup
}
}
Expand Down
1 change: 1 addition & 0 deletions client/apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"test:coverage": "vitest run --coverage"
},
"dependencies": {
"@hatchgrid/logger": "workspace:*",
"@hatchgrid/utilities": "workspace:*",
"@internationalized/date": "^3.8.2",
"@tailwindcss/vite": "^4.1.11",
Expand Down
19 changes: 7 additions & 12 deletions client/apps/web/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
</template>

<script setup>
import { LogManager } from "@hatchgrid/logger";
import { onMounted } from "vue";
import { useI18n } from "vue-i18n";
import { RouterView } from "vue-router";
import Toaster from "./components/ui/sonner/Sonner.vue";
import AppLayout from "./layouts/AppLayout.vue";

const logger = LogManager.getLogger("web:app");
// Initialize CSRF token on app mount
onMounted(async () => {
const MAX_RETRIES = 3;
Expand All @@ -30,16 +32,11 @@ onMounted(async () => {
signal: controller.signal,
});
success = true;
if (import.meta.env.DEV) {
console.log(`CSRF token initialized successfully (attempt ${attempt})`);
}
logger.info(`CSRF token initialized successfully (attempt ${attempt})`);
} catch (error) {
if (import.meta.env.DEV) {
console.error(
`Failed to initialize CSRF token (attempt ${attempt}):`,
error,
);
}
logger.error(`Failed to initialize CSRF token (attempt ${attempt})`, {
error,
});
// Wait briefly before retrying (exponential backoff)
await new Promise((resolve) => setTimeout(resolve, 300 * attempt));
} finally {
Expand All @@ -48,9 +45,7 @@ onMounted(async () => {
}
if (!success) {
// Consider fallback handling in production
if (import.meta.env.DEV) {
console.error("CSRF token initialization failed after maximum retries.");
}
logger.error("CSRF token initialization failed after maximum retries.");
}
});
</script>
6 changes: 3 additions & 3 deletions client/apps/web/src/account/register/register.vue
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
</template>

<script setup lang="ts">
import { LogManager } from "@hatchgrid/logger";
import { toTypedSchema } from "@vee-validate/zod";
import { useForm } from "vee-validate";
import { ref } from "vue";
Expand All @@ -120,6 +121,7 @@ import {
import { Input } from "@/components/ui/input";
import { useAuthStore } from "@/stores/auth";

const logger = LogManager.getLogger("web:register");
const router = useRouter();
const authStore = useAuthStore();
const isLoading = ref(false);
Expand Down Expand Up @@ -179,9 +181,7 @@ const handleRegister = handleSubmit(async (values) => {

await router.push("/login");
} catch (error: unknown) {
if (import.meta.env.DEV) {
console.error("Registration error:", error);
}
logger.error("Registration error", { error });
const description = "Unable to create your account. Please try again.";
toast.error("Registration failed", { description });
} finally {
Expand Down
6 changes: 4 additions & 2 deletions client/apps/web/src/components/UserProfile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
</template>

<script setup lang="ts">
import { LogManager } from "@hatchgrid/logger";
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
// Removed unused AccountService type import
import { useAuthStore } from "@/stores/auth";

const logger = LogManager.getLogger("web:user-profile");
// Use i18n for reactive translations
const { t } = useI18n();

Expand All @@ -57,11 +59,11 @@ const handleLogout = async () => {
try {
await authStore.logoutAsync();
router.push("/");
} catch {
} catch (error) {
// Avoid logging sensitive details
// Show a generic error message to the user (replace with your notification system)
// Example: toast.error("Logout failed. Please try again.");
console.warn("Logout failed");
logger.warn("Logout failed", { error });
} finally {
isLoggingOut.value = false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LogManager } from "@hatchgrid/logger";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { nextTick } from "vue";
import { useLocalStorage } from "../useLocalStorage";
Expand Down Expand Up @@ -90,21 +91,23 @@ describe("useLocalStorage", () => {

it("handles invalid JSON gracefully and returns default value", () => {
const key = "test-key-invalid-json";
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const logger = LogManager.getLogger("web:use-local-storage");
const loggerSpy = vi.spyOn(logger, "warn").mockImplementation(() => {});
Comment on lines +94 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider extracting logger spy setup to reduce duplication.

The logger mocking pattern is correctly implemented and consistent across tests. However, there's repetition in creating the logger instance and spy setup.

Consider extracting the logger spy setup to a helper function or using beforeEach/afterEach:

describe("useLocalStorage", () => {
  const defaultValue = { foo: "bar" };
+ let loggerSpy: ReturnType<typeof vi.spyOn>;

  beforeEach(() => {
    localStorageMock.clear();
    vi.clearAllMocks();
+   const logger = LogManager.getLogger("web:use-local-storage");
+   loggerSpy = vi.spyOn(logger, "warn").mockImplementation(() => {});
  });

  afterEach(() => {
    localStorageMock.clear();
+   loggerSpy?.mockRestore();
  });

Then remove the individual logger setup from each test and just use expect(loggerSpy).toHaveBeenCalled().

Also applies to: 103-104, 109-110, 120-121, 126-127, 139-140

🤖 Prompt for AI Agents
In client/apps/web/src/composables/__tests__/useLocalStorage.spec.ts around
lines 94 to 95 and similarly at lines 103-104, 109-110, 120-121, 126-127, and
139-140, the logger instance creation and spy setup are duplicated across tests.
To fix this, extract the logger creation and spy setup into a shared helper
function or move it into a beforeEach hook to run before each test. Then remove
the individual logger and spy setup from each test case and rely on the shared
setup, using expect(loggerSpy).toHaveBeenCalled() in the tests to verify calls.


// Setup the mock to return invalid JSON
localStorageMock.getItem.mockReturnValueOnce("{invalid-json");

const [state] = useLocalStorage(key, defaultValue);

expect(state.value).toEqual(defaultValue);
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
expect(loggerSpy).toHaveBeenCalled();
loggerSpy.mockRestore();
});

it("handles localStorage errors gracefully", () => {
const key = "test-key-error-get";
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const logger = LogManager.getLogger("web:use-local-storage");
const loggerSpy = vi.spyOn(logger, "warn").mockImplementation(() => {});

// Setup the mock to throw an error
localStorageMock.getItem.mockImplementationOnce(() => {
Expand All @@ -114,13 +117,14 @@ describe("useLocalStorage", () => {
const [state] = useLocalStorage(key, defaultValue);

expect(state.value).toEqual(defaultValue);
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
expect(loggerSpy).toHaveBeenCalled();
loggerSpy.mockRestore();
});

it("handles setItem errors gracefully", async () => {
const key = "test-key-error-set";
const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const logger = LogManager.getLogger("web:use-local-storage");
const loggerSpy = vi.spyOn(logger, "warn").mockImplementation(() => {});

// Setup the mock to throw an error
localStorageMock.setItem.mockImplementationOnce(() => {
Expand All @@ -132,7 +136,7 @@ describe("useLocalStorage", () => {
setState({ foo: "error" });
await nextTick();

expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
expect(loggerSpy).toHaveBeenCalled();
loggerSpy.mockRestore();
});
});
10 changes: 7 additions & 3 deletions client/apps/web/src/composables/useLocalStorage.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LogManager } from "@hatchgrid/logger";
import { type Ref, ref } from "vue";

/**
Expand All @@ -8,6 +9,7 @@ import { type Ref, ref } from "vue";
* @param options - Optional configuration options
* @returns A tuple containing the reactive state and a setter function
*/
const logger = LogManager.getLogger("web:use-local-storage");
export function useLocalStorage<T>(
key: string,
defaultValue: T,
Expand All @@ -24,7 +26,9 @@ export function useLocalStorage<T>(
try {
return JSON.parse(v) as T;
} catch (e) {
console.warn(`Error parsing JSON from localStorage key "${key}":`, e);
logger.warn(`Error parsing JSON from localStorage key "${key}"`, {
error: e,
});
return defaultValue;
}
},
Expand All @@ -40,7 +44,7 @@ export function useLocalStorage<T>(
}
return serializer.read(item);
} catch (error) {
console.warn(`Error reading localStorage key "${key}":`, error);
logger.warn(`Error reading localStorage key "${key}"`, { error });
return defaultValue;
}
}
Expand All @@ -49,7 +53,7 @@ export function useLocalStorage<T>(
try {
localStorage.setItem(key, serializer.write(value));
} catch (error) {
console.warn(`Error setting localStorage key "${key}":`, error);
logger.warn(`Error setting localStorage key "${key}"`, { error });
}
}

Expand Down
Loading
Loading