-
Notifications
You must be signed in to change notification settings - Fork 0
Overview almost complete #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… enhance sunset API documentation
This commit adds runtime configuration for the anime and sunset API URLs. The animeUrl and sunsetUrl properties are now included in the runtimeConfig object in the nuxt.config.ts file. This allows for more flexibility and easier maintenance of the API URLs used in the code.
…omponent Integrates the quotes API endpoint and fetches a random quote to display in the quote component.
Reviewer's Guide by SourceryThis pull request introduces a significant expansion of the application's features and styling. It includes new modules, plugins, components, API endpoints, and data management strategies. The changes cover a wide range of areas, including UI components, data fetching, state management, and theming. Sequence diagram for fetching ratessequenceDiagram
participant Client
participant RatesStore
participant API Endpoint
participant Currency API
Client->>RatesStore: fetchRates(body)
RatesStore->>API Endpoint: POST /api/finance/rates with body
API Endpoint->>Currency API: GET currency data
Currency API-->>API Endpoint: Returns currency data
API Endpoint-->>RatesStore: Returns rates data
RatesStore-->>Client: Updates rates
Sequence diagram for fetching imagessequenceDiagram
participant Client
participant AnimeImageStore
participant API Endpoint
participant Anime API
Client->>AnimeImageStore: fetchImages()
AnimeImageStore->>API Endpoint: GET /api/anime/images
API Endpoint->>Anime API: GET image data
Anime API-->>API Endpoint: Returns image data
API Endpoint-->>AnimeImageStore: Returns images data
AnimeImageStore-->>Client: Updates images
Sequence diagram for fetching sunset datasequenceDiagram
participant Client
participant TimeMetrics Component
participant API Endpoint
participant Sunset API
Client->>TimeMetrics Component: Mounts
TimeMetrics Component->>API Endpoint: POST /api/weather/sunset with location data
API Endpoint->>Sunset API: GET sunset data
Sunset API-->>API Endpoint: Returns sunset data
API Endpoint-->>TimeMetrics Component: Returns sunset data
TimeMetrics Component-->>Client: Updates UI with sunset data
Class diagram for DayInfoclassDiagram
class DayInfo {
+results: Results
+status: string
+tzid: string
}
class Results {
+sunrise: string
+sunset: string
+solar_noon: string
+day_length: number
+civil_twilight_begin: string
+civil_twilight_end: string
+nautical_twilight_begin: string
+nautical_twilight_end: string
+astronomical_twilight_begin: string
+astronomical_twilight_end: string
}
DayInfo -- Results : contains
Class diagram for CurrencyBeaconResponseclassDiagram
class CurrencyBeaconResponse {
+meta: Meta
+response: Response
+date: string
+base: string
+rates: Rates
}
class Meta {
+code: number
+disclaimer: string
}
class Response {
+date: string
+base: string
+rates: Rates
}
class Rates {
+ [quote: string]: number
}
CurrencyBeaconResponse -- Meta : has
CurrencyBeaconResponse -- Response : has
CurrencyBeaconResponse -- Rates : has
Response -- Rates : has
Class diagram for NekoImageclassDiagram
class NekoImage {
+artist_href: string
+artist_name: string
+source_url: string
+url: string
}
Class diagram for PairRateclassDiagram
class PairRate {
+pair: string
+rate: number
}
Class diagram for QuoteclassDiagram
class Quote {
+author: string
+quote: string
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
WalkthroughThis update introduces a comprehensive overhaul and expansion of the application's UI, state management, API integration, and theming infrastructure. It adds numerous new Vue components for dashboards, device and financial data, and utility UI elements, alongside new Pinia stores for managing anime images and currency rates with localStorage caching. Several new API endpoints are implemented for fetching anime images, currency rates, quotes, and sunrise/sunset data, all using standardized server response types. The configuration for Nuxt, Tailwind, and theming is refactored to support advanced theming, font management, and plugin integration. Multiple font and license files are removed, with new font configuration approaches adopted. Supporting utility libraries and CSS files are added, and package dependencies are significantly expanded to accommodate these features. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI_Component
participant Pinia_Store
participant API_Endpoint
participant External_API
User->>UI_Component: Interacts (e.g., loads dashboard)
UI_Component->>Pinia_Store: Fetch data (e.g., images, rates)
Pinia_Store->>Pinia_Store: Check localStorage for cache
alt Cache valid
Pinia_Store->>UI_Component: Return cached data
else Cache missing/expired
Pinia_Store->>API_Endpoint: Request fresh data
API_Endpoint->>External_API: Fetch from external service
External_API-->>API_Endpoint: Return data
API_Endpoint->>Pinia_Store: Return standardized response
Pinia_Store->>UI_Component: Return new data
Pinia_Store->>Pinia_Store: Update cache
end
UI_Component->>User: Display data (with loading states)
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @michaelnji - I've reviewed your changes - here's some feedback:
Overall Comments:
- Consider using environment variables instead of hardcoding API URLs and keys in
nuxt.config.ts
. - The addition of many new dependencies in
package.json
might increase the bundle size - consider if they're all necessary.
Here's what I looked at during the review
- 🟡 General issues: 3 issues found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
const item = db.getItem(key); | ||
return item !== null ? JSON.parse(item) : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion (bug_risk): Consider adding error handling when parsing JSON from localStorage.
If the stored item is corrupted or not valid JSON, JSON.parse may throw an error. A try/catch block would make the function more robust in such scenarios.
const item = db.getItem(key); | |
return item !== null ? JSON.parse(item) : null; | |
const item = db.getItem(key); | |
if (item !== null) { | |
try { | |
return JSON.parse(item); | |
} catch (error) { | |
console.error(`Error parsing JSON for key ${key}:`, error); | |
return null; | |
} | |
} | |
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 48
🧹 Nitpick comments (48)
public/fonts/terminator-real-nfi/N Freeware License.txt (6)
1-3
: Standardize license header formatting
The separator on line 2 (=
) is minimal and may not clearly distinguish sections. Consider using a more descriptive header or a longer divider (e.g.,==== License Agreement ====
, or a line of dashes) for improved readability and consistency.
4-6
: Clarify usage scope
The phrase “for all your private purposes” on line 5 is informal. To align with common licensing terminology, consider rephrasing to something like:
“This font is free for personal, non‑commercial use.”
7-9
: Fix currency formatting and simplify sentence
On line 8, “U$ 25” is non‑standard—use “US$ 25” (no space) or “$25”. Also, streamline the clause to:
“For commercial use, please purchase this font for US$ 25.”
10-12
: Adjust capitalization and punctuation
- Change “Thank You.” (line 10) to “Thank you.” for consistent sentence casing.
- The lone equals sign on line 12 duplicates the header divider; ensure it matches your chosen style or remove if unnecessary.
13-15
: Capitalize label and enforce secure URL
- Capitalize “website:” (line 13) to “Website:”.
- Verify that “http://www.norfok.com” (line 14) is correct and consider switching to HTTPS if supported (e.g.,
https://www.norfok.com
).
16-17
: Remove extraneous trailing blank lines
Trim extra empty lines at the end of the file—ensure there’s exactly one newline at EOF to adhere to file formatting best practices.assets/css/custom.css (1)
9-15
: Good variant without shadow.This alternate class provides a nice option when the shadow effect isn't desired. Consider combining these classes with a shared base class to reduce duplication.
+.glass-base { + /* From https://css.glass */ + background: rgba(255, 255, 255, 0.37); + backdrop-filter: blur(7.1px); + -webkit-backdrop-filter: blur(7.1px); +} + .glass { /* From https://css.glass */ - background: rgba(255, 255, 255, 0.37); + @extend .glass-base; box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(7.1px); - -webkit-backdrop-filter: blur(7.1px); } .glass-no-shadow { /* From https://css.glass */ - background: rgba(255, 255, 255, 0.37); - - backdrop-filter: blur(7.1px); - -webkit-backdrop-filter: blur(7.1px); + @extend .glass-base; }server/utils/response.ts (2)
31-33
: Remove unnecessary blank lines.These blank lines don't serve any purpose and can be removed to improve code readability.
} - - - /**
55-65
: Simplify error response construction.The error response object construction can be simplified for better readability.
const res = { status, message, - ok: false - + ok: false } return res + // Or even simpler: + // return { status, message, ok: false }; }lib/dbManager.ts (2)
28-35
: Simplify getItemValue function.The function has a redundant check for null. It checks
db.getItem(key) !== null
twice (line 30 and implicitly in line 32).export function getItemValue(key: string) { const db = localStorage; - if (db.getItem(key) !== null) { - const item = db.getItem(key); - return item !== null ? JSON.parse(item) : null; - } - return null; + const item = db.getItem(key); + return item !== null ? JSON.parse(item) : null; }
44-50
: Fix indentation and add type validation in setItemValue function.The indentation at line 49 is inconsistent. Also, the function doesn't validate that the key is a string, only that it's not null.
export function setItemValue(key: string, value: unknown = "") { - if (key !== null) { + if (key && typeof key === "string") { localStorage.setItem(key, JSON.stringify(value)); return value; } - return null; + return null; }components/football.vue (1)
22-24
: Add responsive styling considerationsThe current background gradient is fixed. Consider adding responsive styling variations or adjusting the gradient for different screen sizes.
.bg-foot{ background:linear-gradient(180deg, #121317, #000000); + @media (max-width: 768px) { + background:linear-gradient(160deg, #121317, #000000); + } }components/overviewTab.vue (2)
1-3
: Add reactive state for component managementThe script section is currently empty. Consider adding reactive state to manage the visibility or loading state of child components.
<script lang="ts" setup> - +import { ref } from 'vue' + +// Track loading states for child components +const componentsLoading = ref({ + timeMetrics: true, + watchlist: true, + quote: true, + picOfDay: true +}) + +// Function to update loading state +const setComponentLoaded = (component: keyof typeof componentsLoading) => { + componentsLoading.value[component] = false +} </script>
6-17
: Improve grid layout for better responsivenessThe current grid layout might not be optimal for all viewport sizes. Consider adding more breakpoints or adjusting the grid based on content priority.
- <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4"> + <div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-3 lg:gap-4"> <div class="space-y-4"> <DeviceInfo /> <TimeMetrics/> <Football/> </div> - <Watchlist /> + <div class="h-full"> + <Watchlist /> + </div> <div class="space-y-4"> <Quote /> <PicOfDay/> </div> </div>components/clock.vue (3)
19-19
: Check the Tailwind class nameThe class
mt2
appears to be a typo and should likely bemt-2
to follow Tailwind CSS naming conventions.- <div class="flex mt2 items-center justify-between gap-x-6"> + <div class="flex mt-2 items-center justify-between gap-x-6">
6-11
: Consider adding AM/PM indicator to the time displayThe time format string uses 'hh:mm' which is 12-hour format, but there's no AM/PM indicator. This could be confusing for users.
-const formatStr = 'hh:mm' +const formatStr = 'hh:mm a' // Adds AM/PM indicator
33-52
: Consider using CSS variables for text shadow valuesThe text shadow implementation is verbose with repetitive values. Using CSS variables would make this more maintainable and easier to adjust for different themes.
<style scoped> +:root { + --shadow-distance: 3px; + --shadow-color: #000000; + --shadow-bottom: 15px; +} h1.clock { color: #fff; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - text-shadow: 0px -3px 0 #000000, - 0px -3px 0 #000000, - 0px 3px 0 #000000, - 0px 3px 0 #000000, - -3px 0px 0 #000000, - 3px 0px 0 #000000, - -3px 0px 0 #000000, - 3px 0px 0 #000000, - -3px -3px 0 #000000, - 3px -3px 0 #000000, - -3px 3px 0 #000000, - 3px 3px 0 #000000, - -3px 15px 0 #000000, - 0px 15px 0 #000000, - 3px 15px 0 #000000; + text-shadow: + 0px calc(-1 * var(--shadow-distance)) 0 var(--shadow-color), + 0px var(--shadow-distance) 0 var(--shadow-color), + calc(-1 * var(--shadow-distance)) 0px 0 var(--shadow-color), + var(--shadow-distance) 0px 0 var(--shadow-color), + calc(-1 * var(--shadow-distance)) calc(-1 * var(--shadow-distance)) 0 var(--shadow-color), + var(--shadow-distance) calc(-1 * var(--shadow-distance)) 0 var(--shadow-color), + calc(-1 * var(--shadow-distance)) var(--shadow-distance) 0 var(--shadow-color), + var(--shadow-distance) var(--shadow-distance) 0 var(--shadow-color), + calc(-1 * var(--shadow-distance)) var(--shadow-bottom) 0 var(--shadow-color), + 0px var(--shadow-bottom) 0 var(--shadow-color), + var(--shadow-distance) var(--shadow-bottom) 0 var(--shadow-color); } </style>server/api/anime/images.get.ts (2)
11-15
: Implement caching to reduce external API callsConsider implementing a caching strategy to reduce the number of external API calls and improve performance.
You could use an in-memory cache or leverage the Nitro cache capabilities:
+import { defineEventHandler, setResponseStatus, getHeader, setCacheHeader } from 'h3'; + export default eventHandler(async (event) => { try{ const config = useRuntimeConfig(); const constructedUrl = `${config.animeUrl}/waifu?amount=3`; const resp = await $fetch(constructedUrl); const data = resp as { results: NekoImage[] }; + // Set cache headers for 1 hour + setCacheHeader(event, { + maxAge: 3600, + staleWhileRevalidate: 86400 + }); + setResponseStatus(event, 200); return sendServerResponse(200, "success", data.results);
16-25
: Improve error handling with more specific error informationThe current error handling could be improved by providing more specific error information for debugging.
} catch (error) { setResponseStatus(event, 500); let message: string; if (error instanceof Error && error.message.includes("fetch failed")) { message = "fetch failed"; + console.error("Error fetching anime images:", error.message); } else { - message = "Internal Server Error"; + message = error instanceof Error ? error.message : "Internal Server Error"; + console.error("Error in anime images API:", error); } return sendServerResponse(500, message); }server/api/quotes.get.ts (1)
21-24
: Implement caching to improve performanceConsider implementing a caching strategy to reduce external API calls and improve performance.
+import { defineEventHandler, setResponseStatus, setCacheHeader } from 'h3'; export default eventHandler(async (event) => { try { const config = useRuntimeConfig(); // ...fetchQuote implementation... const quoteData = await fetchQuote(); + // Set cache headers for 6 hours + setCacheHeader(event, { + maxAge: 21600, + staleWhileRevalidate: 86400 + }); + setResponseStatus(event, 200); return sendServerResponse(200, "success", quoteData);app.vue (1)
21-23
: Document the purpose of background classesAdd comments to explain the intended use case for each background gradient to help other developers.
+/* Light pastel green gradient - used for the main background */ .bg-2 { background: linear-gradient(135deg, #F8FF8Fff, #F3FF96ff, #EFFF9Dff, #EAFFA4ff, #E5FFAAff, #E0FFB1ff, #DCFFB8ff, #D7FFBFff, #D2FFC6ff, #CDFFCDff, #C9FFD3ff, #C4FFDAff, #BFFFE1ff); }
components/tabManager.vue (1)
26-29
: Fix function parameter formatting for consistency.The function definition has an unusual line break between the parameter name and closing parenthesis. This affects code readability and consistency.
-function changeTabs(tab: string -) { +function changeTabs(tab: string) { emit("tab-change", tab) }server/api/weather/sunset.post.ts (3)
53-53
: Fix inconsistent indentation.There appears to be inconsistent indentation with tab characters on this line.
- const constructedUrl = `${config.sunsetUrl}?lat=${body.lat}&lng=${body.lng}&formatted=${body.formatted}&date=${body.date}&tzid=${body.tzid}`; + const constructedUrl = `${config.sunsetUrl}?lat=${body.lat}&lng=${body.lng}&formatted=${body.formatted}&date=${body.date}&tzid=${body.tzid}`;
44-51
: Consider adding value range validation for coordinates.While type validation is good, consider also validating value ranges (latitude: -90 to 90, longitude: -180 to 180) to catch invalid geographic coordinates early.
if (typeof body.lat !== 'number' || typeof body.lng !== 'number' || typeof body.date !== 'string' || typeof body.tzid !== 'string' || - (body.formatted !== 0 && body.formatted !== 1)) { + (body.formatted !== 0 && body.formatted !== 1) || + body.lat < -90 || body.lat > 90 || + body.lng < -180 || body.lng > 180) { setResponseStatus(event, 400) return sendServerResponse(400, 'Invalid payload, fields have incorrect types') }
55-56
: Consider adding a timeout for the external API call.The external API call lacks a timeout setting. Consider adding one to prevent hanging requests if the external service is slow or unresponsive.
- const resp = await $fetch(constructedUrl) + const resp = await $fetch(constructedUrl, { timeout: 10000 }) // 10 second timeout const data = resp as DayInfocomponents/deviceInfo.vue (4)
1-7
: Consolidate multiple imports from the same package.Since you're importing multiple functions from '@vueuse/core', consider consolidating them into a single import statement for better readability.
-import { useMemory } from '@vueuse/core' -import { useBattery } from '@vueuse/core' -import { useFps } from '@vueuse/core' -import { useNetwork } from '@vueuse/core' +import { useMemory, useBattery, useFps, useNetwork } from '@vueuse/core'
7-7
: Remove unused variables from destructuring assignment.Several variables from the
useNetwork()
call are not used in the component. Remove them to improve code clarity and performance.-const { isOnline, offlineAt, downlink, downlinkMax, effectiveType, saveData, type } = useNetwork() +const { effectiveType } = useNetwork()
10-13
: Remove unused function.The
size()
function is defined but never used in the component. Remove it to keep the code clean.-function size(v: number) { - const kb = v / 1024 / 1024 - return `${kb.toFixed(2)} MB` -}
15-15
: Remove unused battery variables.Only
charging
andlevel
from the battery API are used in the template. Remove the unused variables.-const { charging, chargingTime, dischargingTime, level } = useBattery() +const { charging, level } = useBattery()components/quote.vue (2)
23-32
: Consider adding fallback content when loading failsThe template shows a loading state but doesn't handle the case where loading fails or no quote is available. Consider showing a fallback message instead of an empty component.
Add a condition to display a fallback message:
<div> <div class="rounded-3xl h12rem bg-white p6"> <h3 class="uppercase opacity-80 text-lg mb2">Quote of the day</h3> <Skeleton v-if="isLoading" class="w-full h-2rem bg-stone-200 rounded-xl" /> <Skeleton v-if="isLoading" class="w-full h-2rem mt2 bg-stone-200 rounded-xl" /> <Skeleton v-if="isLoading" class="w-full h-2rem mt2 bg-stone-200 rounded-xl" /> <h2 class="text-3xl font-bold" v-if="!isLoading"> {{ quote }} </h2> + <h2 class="text-3xl font-bold" v-if="!isLoading && !quote"> + No quote available today. Check back later! + </h2> </div> </div>
23-24
: Standardize CSS class namingThe component uses unconventional CSS class naming without hyphens between property and value (e.g., "h12rem", "p6" instead of "h-12rem", "p-6").
Consider using standard Tailwind CSS class naming conventions with hyphens:
-<div class="rounded-3xl h12rem bg-white p6"> +<div class="rounded-3xl h-12rem bg-white p-6">components/picOfDay.vue (1)
39-42
: Address commented-out codeThe component contains commented-out code for displaying artist names. Either implement this feature or remove the commented code to keep the codebase clean.
Either remove the commented code or uncomment and implement the feature:
-<!-- <div v-if="!isLoading && animeImageStore.images" class="mt1"> - <h2 class="text-base font-extrabold">{{ animeImageStore.images[0].artist_name }}</h2> -</div> -->components/ui/progress/Progress.vue (1)
35-39
: Consider simplifying the null check in the transform style.The null coalescing operator on line 38 is redundant since you've already defaulted modelValue to 0 in the props definition.
- :style="`transform: translateX(-${100 - (props.modelValue ?? 0)}%);`" + :style="`transform: translateX(-${100 - props.modelValue}%);`"assets/css/tailwind.css (1)
1-6
: Good setup of Tailwind CSS with custom variant.The imports and custom variant for dark mode are well-defined. However, there's a commented-out import on line 5 that should either be removed or uncommented if needed.
@import "tailwindcss"; @import "tw-animate-css"; @custom-variant dark (&:is(.dark *)); -/* @import "tailwindcss-primeui"; */
pages/index.vue (2)
1-11
: Good implementation of time-based greeting logic.The greeting logic effectively uses date-fns and the useNow composable to display time-appropriate greetings. However, consider extracting the hardcoded name "Michael" to a configuration variable or user profile for better flexibility.
- if (currentHour < 13 && currentHour >= 0) return 'Good morning, Michael' - if (currentHour < 17 && currentHour >= 13) return 'Good day, Michael' - if (currentHour < 19 && currentHour >= 17) return 'Good evening, Michael' - return 'Good night, Michael' + const userName = useUserStore().name || 'Michael' // Or from any appropriate source + if (currentHour < 13 && currentHour >= 0) return `Good morning, ${userName}` + if (currentHour < 17 && currentHour >= 13) return `Good day, ${userName}` + if (currentHour < 19 && currentHour >= 17) return `Good evening, ${userName}` + return `Good night, ${userName}`
34-36
: Remove empty style tags.The style tags are empty and can be removed to keep the code clean.
-<style> - -</style>components/ui/button/index.ts (1)
5-7
: Very long base class string could be more readable.The base class string in the cva function is quite long, making it harder to read and maintain. Consider breaking it down into multiple concatenated strings for better readability.
-export const buttonVariants = cva( - 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive', +export const buttonVariants = cva( + [ + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium', + 'transition-all disabled:pointer-events-none disabled:opacity-50', + '[&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0', + 'outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]', + 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive' + ].join(' '),stores/anime-images.store.ts (2)
31-49
: Fix indentation inconsistencies and add error handlingThere are indentation inconsistencies in the
fetchImagesFromStorage
function, and it also lacks error handling similar tofetchImages
.Fix the indentation and add error handling:
const fetchImagesFromStorage = async () => { + isLoading.value = true; + error.value = null; + try { const imagesFromStorage = getItemValue('neko-img') as { data: NekoImage[]; timestamp: Date; expires: Date; } | null; if (imagesFromStorage) { if (isFuture(imagesFromStorage.expires)) { images.value = imagesFromStorage.data; } else { // If the images are expired, fetch new images - await fetchImages(); + await fetchImages(); } } else { // If there are no images in storage, fetch new images - await fetchImages(); + await fetchImages(); } + } catch (e) { + error.value = e instanceof Error ? e.message : 'Failed to fetch images from storage'; + console.error('Error fetching images from storage:', e); + } finally { + isLoading.value = false; + } };
23-27
: Replace useNow() with standard Date constructorUsing
useNow().value
is more verbose than needed and potentially less efficient than using the standardDate
constructor.setItemValue("neko-img", { data: response.data, - timestamp: useNow().value, - expires: addHours(useNow().value, 6), + timestamp: new Date(), + expires: addHours(new Date(), 6), });server/api/finance/rates.post.ts (1)
78-89
: Use specific error types for better error handlingThe error handling is good, but could be improved by using specific error types or codes to help the client better understand the nature of the failure.
Enhance error type reporting:
catch (error) { setResponseStatus(event, 500); let message: string; + let errorCode = 'UNKNOWN_ERROR'; if (error instanceof Error) { message = error.message; + // Add specific error identification + if (message.includes('fetch failed')) { + errorCode = 'NETWORK_ERROR'; + message = 'fetch failed'; + } else if (message.includes('rate limit')) { + errorCode = 'RATE_LIMIT_EXCEEDED'; + } else if (message.includes('invalid api key')) { + errorCode = 'INVALID_API_KEY'; + } } else { message = String(error); } return sendServerResponse( 500, - message.includes("fetch failed") ? "fetch failed" : message, + message, + null, + { errorCode } ); }stores/rates.store.ts (1)
25-29
: Use standard Date constructor instead of useNow()Using
useNow().value
is more verbose than necessary and potentially less efficient.setItemValue("rates", { data: response.data, - timestamp: useNow().value, - expires: addHours(useNow().value, 1), + timestamp: new Date(), + expires: addHours(new Date(), 1), });components/timeMetrics.vue (2)
25-25
: Remove debug console.log statementsThis console.log should be removed before production deployment.
- console.log(coords.value)
64-65
: Remove unnecessary empty linesThere are multiple empty lines at the end of the file that should be removed.
- -components/watchlist.vue (2)
2-2
: Remove unused importsThe component imports types that aren't directly used in the component.
-import type { PairRate, StatusCode, ServerResponse } from '~/server/types/index.types' +import type { PairRate } from '~/server/types/index.types'
97-105
: Consider implementing or removing commented codeThe commented-out code suggests a plan to show rate changes with directional indicators, but this functionality isn't implemented yet. Consider either implementing this feature or removing the commented code.
- <!-- <div class="flex items-end gap-x-1"> - <Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" /> - <Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" /> - <p class="text-lg mt2 font-bold" - :class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{ - item.change - }}%</p> - - </div> -->server/types/index.types.ts (3)
35-42
: Remove unnecessary empty lines in NekoImage typeThere are unnecessary empty lines in the NekoImage type definition.
export type NekoImage = { - artist_href: string, artist_name: string, source_url: string, url: string - };
54-55
: Simplify error codes definitionThe
ErrorCodes
type is redundant as it's justStatusCode
minus 200. It could be defined more concisely.export type StatusCode = 200 | 400 | 404 | 403 | 500 | 401; -export type ErrorCodes = 400 | 404 | 403 | 500 | 401; +export type ErrorCodes = Exclude<StatusCode, 200>; export type ServerData = null | DayInfo | NekoImage[] | PairRate[] | Quote;
56-65
: Add JSDoc documentation to ServerResponse interfaceThe
ServerResponse
interface uses conditional types which can be difficult to understand without documentation.+/** + * Generic server response interface that handles both successful and error responses + * @template Status - The HTTP status code + * @template Data - The payload data type + */ export interface ServerResponse< Status extends StatusCode, Data extends ServerData, > { status: Status; message?: string; ok: Status extends 200 ? true : false; data?: Status extends 200 ? Data : null; error?: Status extends 200 ? null : string; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (210)
bun.lock
is excluded by!**/*.lock
public/fonts/Acro Mono DCU/Acro Mono DCU/- Desktop Commercial Use License - Pixel Surplus.pdf
is excluded by!**/*.pdf
public/fonts/Acro Mono DCU/Acro Mono DCU/AcroMono.otf
is excluded by!**/*.otf
public/fonts/Acro Mono DCU/Acro Mono DCU/AcroMono.ttf
is excluded by!**/*.ttf
public/fonts/Acro Mono DCU/__MACOSX/Acro Mono DCU/._- Desktop Commercial Use License - Pixel Surplus.pdf
is excluded by!**/*.pdf
public/fonts/Acro Mono DCU/__MACOSX/Acro Mono DCU/._AcroMono.otf
is excluded by!**/*.otf
public/fonts/Acro Mono DCU/__MACOSX/Acro Mono DCU/._AcroMono.ttf
is excluded by!**/*.ttf
public/fonts/Adblox DCU/Adblox DCU/- Desktop Commercial Use License - Pixel Surplus.pdf
is excluded by!**/*.pdf
public/fonts/Adblox DCU/Adblox DCU/Adblox.otf
is excluded by!**/*.otf
public/fonts/Adblox DCU/__MACOSX/Adblox DCU/._- Desktop Commercial Use License - Pixel Surplus.pdf
is excluded by!**/*.pdf
public/fonts/Adblox DCU/__MACOSX/Adblox DCU/._Adblox.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Bold.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Extralight.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Light.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Medium.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Regular.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/OTF/ClashDisplay-Semibold.otf
is excluded by!**/*.otf
public/fonts/ClashDisplay_Complete/Fonts/TTF/ClashDisplay-Variable.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Bold.woff2
is excluded by!**/*.woff2
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Extralight.woff2
is excluded by!**/*.woff2
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Light.woff2
is excluded by!**/*.woff2
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Medium.woff2
is excluded by!**/*.woff2
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Regular.woff2
is excluded by!**/*.woff2
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Semibold.woff2
is excluded by!**/*.woff2
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.eot
is excluded by!**/*.eot
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.ttf
is excluded by!**/*.ttf
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.woff
is excluded by!**/*.woff
public/fonts/ClashDisplay_Complete/Fonts/WEB/fonts/ClashDisplay-Variable.woff2
is excluded by!**/*.woff2
public/fonts/Khaviax.otf
is excluded by!**/*.otf
public/fonts/LAGISTHA.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Black.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-BlackItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Bold.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-BoldItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraBold.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraBoldItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraLight.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ExtraLightItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Italic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Light.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-LightItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Medium.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-MediumItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Regular.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-SemiBold.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-SemiBoldItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-Thin.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/OTF/Poppins-ThinItalic.otf
is excluded by!**/*.otf
public/fonts/Poppins_Complete/Fonts/TTF/Poppins-Variable.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/TTF/Poppins-VariableItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Black.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BlackItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Bold.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-BoldItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBold.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraBoldItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLight.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ExtraLightItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Italic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Light.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-LightItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Medium.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-MediumItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Regular.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBold.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-SemiBoldItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Thin.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-ThinItalic.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-Variable.woff2
is excluded by!**/*.woff2
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.eot
is excluded by!**/*.eot
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.ttf
is excluded by!**/*.ttf
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.woff
is excluded by!**/*.woff
public/fonts/Poppins_Complete/Fonts/WEB/fonts/Poppins-VariableItalic.woff2
is excluded by!**/*.woff2
public/fonts/UT Defghio Nixav - Personal Use Only/- Free Personal Use License - Pixel Surplus.pdf
is excluded by!**/*.pdf
public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-PS/UT Defghio Nixav.otf
is excluded by!**/*.otf
public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-PS/UT Defghio Nixav.woff
is excluded by!**/*.woff
public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-PS/UT Defghio Nixav.woff2
is excluded by!**/*.woff2
public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-TT/UT Defghio Nixav.ttf
is excluded by!**/*.ttf
public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-TT/UT Defghio Nixav.woff
is excluded by!**/*.woff
public/fonts/UT Defghio Nixav - Personal Use Only/UT Defghio Nixav - universitype/OpenType-TT/UT Defghio Nixav.woff2
is excluded by!**/*.woff2
public/fonts/action-man/Action_Man.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Bold.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Bold_Italic.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Extended.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Extended_Bold.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Extended_Bold_Italic.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Extended_Italic.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Italic.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Shaded.ttf
is excluded by!**/*.ttf
public/fonts/action-man/Action_Man_Shaded_Italic.ttf
is excluded by!**/*.ttf
public/fonts/espial/EspialRegular15-6Y08Y.otf
is excluded by!**/*.otf
public/fonts/fifa-welcome/fifawelcome.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Black.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-BlackItalic.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Bold.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-BoldItalic.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Italic.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Light.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-LightItalic.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Medium.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-MediumItalic.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Regular.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-Thin.ttf
is excluded by!**/*.ttf
public/fonts/google-sans/ProductSans-ThinItalic.ttf
is excluded by!**/*.ttf
public/fonts/handbuck/Handbuck-Regular.eot
is excluded by!**/*.eot
public/fonts/handbuck/Handbuck-Regular.ttf
is excluded by!**/*.ttf
public/fonts/handbuck/Handbuck-Regular.woff
is excluded by!**/*.woff
public/fonts/handbuck/Handbuck-Regular.woff2
is excluded by!**/*.woff2
public/fonts/intro/Intro.otf
is excluded by!**/*.otf
public/fonts/intro/IntroDemoBlackCapsInline.otf
is excluded by!**/*.otf
public/fonts/japanese/Japanese.ttf
is excluded by!**/*.ttf
public/fonts/panton/FFF_EULA_license.pdf
is excluded by!**/*.pdf
public/fonts/panton/Panton-BlackCaps.otf
is excluded by!**/*.otf
public/fonts/panton/Panton-BlackitalicCaps.otf
is excluded by!**/*.otf
public/fonts/panton/Panton-LightCaps.otf
is excluded by!**/*.otf
public/fonts/panton/Panton-LightitalicCaps.otf
is excluded by!**/*.otf
public/fonts/pixeled/Pixeled.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-Bold.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-BoldItalic.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-Italic.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-Light.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-LightItalic.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-Medium.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-MediumItalic.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-Regular.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-Thin.ttf
is excluded by!**/*.ttf
public/fonts/roboto-mono/RobotoMono-ThinItalic.ttf
is excluded by!**/*.ttf
public/fonts/terminator-real-nfi/opentype/terminator real nfi.otf
is excluded by!**/*.otf
public/fonts/terminator-real-nfi/opentype/terminator real nfi.ttf
is excluded by!**/*.ttf
public/fonts/terminator-real-nfi/truetype/terminator real nfi.ttf
is excluded by!**/*.ttf
public/fonts/uni-sans/FFF_EULA_license.pdf
is excluded by!**/*.pdf
public/fonts/uni-sans/Uni Sans Heavy Italic.otf
is excluded by!**/*.otf
public/fonts/uni-sans/Uni Sans Heavy.otf
is excluded by!**/*.otf
public/fonts/uni-sans/Uni Sans Thin Italic.otf
is excluded by!**/*.otf
public/fonts/uni-sans/Uni Sans Thin.otf
is excluded by!**/*.otf
📒 Files selected for processing (44)
app.vue
(1 hunks)assets/css/custom.css
(1 hunks)assets/css/tailwind.css
(1 hunks)components.json
(1 hunks)components/clock.vue
(1 hunks)components/deviceInfo.vue
(1 hunks)components/football.vue
(1 hunks)components/overviewTab.vue
(1 hunks)components/picOfDay.vue
(1 hunks)components/quote.vue
(1 hunks)components/tabManager.vue
(1 hunks)components/timeMetrics.vue
(1 hunks)components/ui/badge/Badge.vue
(1 hunks)components/ui/badge/index.ts
(1 hunks)components/ui/button/Button.vue
(1 hunks)components/ui/button/index.ts
(1 hunks)components/ui/progress/Progress.vue
(1 hunks)components/ui/progress/index.ts
(1 hunks)components/ui/skeleton/Skeleton.vue
(1 hunks)components/ui/skeleton/index.ts
(1 hunks)components/watchlist.vue
(1 hunks)lib/dbManager.ts
(1 hunks)nuxt.config.ts
(1 hunks)package.json
(1 hunks)pages/index.vue
(1 hunks)public/fonts/ClashDisplay_Complete/Fonts/WEB/README.md
(0 hunks)public/fonts/ClashDisplay_Complete/Fonts/WEB/css/clash-display.css
(0 hunks)public/fonts/ClashDisplay_Complete/License/FFL.txt
(0 hunks)public/fonts/Poppins_Complete/Fonts/WEB/README.md
(0 hunks)public/fonts/Poppins_Complete/Fonts/WEB/css/poppins.css
(0 hunks)public/fonts/Poppins_Complete/License/OFL.txt
(0 hunks)public/fonts/handbuck/stylesheet.css
(0 hunks)public/fonts/terminator-real-nfi/N Freeware License.txt
(1 hunks)server/api/anime/images.get.ts
(1 hunks)server/api/finance/rates.post.ts
(1 hunks)server/api/quotes.get.ts
(1 hunks)server/api/weather/sunset.post.ts
(1 hunks)server/types/index.types.ts
(1 hunks)server/utils/response.ts
(1 hunks)stores/anime-images.store.ts
(1 hunks)stores/rates.store.ts
(1 hunks)tailwind.config.ts
(1 hunks)theme.config.ts
(1 hunks)uno.config.ts
(1 hunks)
💤 Files with no reviewable changes (7)
- public/fonts/handbuck/stylesheet.css
- public/fonts/ClashDisplay_Complete/Fonts/WEB/README.md
- public/fonts/Poppins_Complete/Fonts/WEB/README.md
- public/fonts/Poppins_Complete/License/OFL.txt
- public/fonts/Poppins_Complete/Fonts/WEB/css/poppins.css
- public/fonts/ClashDisplay_Complete/Fonts/WEB/css/clash-display.css
- public/fonts/ClashDisplay_Complete/License/FFL.txt
🧰 Additional context used
🧬 Code Graph Analysis (9)
uno.config.ts (1)
theme.config.ts (1)
userTheme
(2-79)
tailwind.config.ts (1)
theme.config.ts (1)
userTheme
(2-79)
server/api/quotes.get.ts (2)
server/types/index.types.ts (1)
Quote
(48-51)server/utils/response.ts (1)
sendServerResponse
(42-65)
server/api/weather/sunset.post.ts (2)
server/utils/response.ts (1)
sendServerResponse
(42-65)server/types/index.types.ts (1)
DayInfo
(1-16)
server/api/anime/images.get.ts (2)
server/types/index.types.ts (1)
NekoImage
(35-42)server/utils/response.ts (1)
sendServerResponse
(42-65)
server/api/finance/rates.post.ts (2)
server/utils/response.ts (1)
sendServerResponse
(42-65)server/types/index.types.ts (1)
CurrencyBeaconResponse
(17-34)
stores/anime-images.store.ts (2)
server/types/index.types.ts (3)
NekoImage
(35-42)ServerResponse
(56-65)StatusCode
(53-53)lib/dbManager.ts (2)
setItemValue
(44-50)getItemValue
(28-35)
stores/rates.store.ts (2)
server/types/index.types.ts (3)
PairRate
(43-46)ServerResponse
(56-65)StatusCode
(53-53)lib/dbManager.ts (2)
setItemValue
(44-50)getItemValue
(28-35)
server/utils/response.ts (1)
server/types/index.types.ts (3)
ErrorCodes
(54-54)ServerData
(55-55)StatusCode
(53-53)
🔇 Additional comments (28)
assets/css/custom.css (1)
1-7
: Nice implementation of the glassmorphism effect with proper vendor prefixes.The
.glass
class correctly implements the frosted glass effect with background transparency, box-shadow, and backdrop filter blur. The vendor prefix (-webkit-backdrop-filter) ensures cross-browser compatibility.components.json (1)
1-20
: Well-structured ShadCN Vue configuration.The configuration is properly set up with appropriate schema reference, TypeScript support, Tailwind CSS settings, path aliases, and icon library configuration. This provides a solid foundation for the UI components in your application.
The setup using the "new-york" style and "stone" base color with CSS variables will create a consistent design system. The path aliases will make imports cleaner throughout the codebase.
server/utils/response.ts (3)
1-2
: Good use of TypeScript imports for type safety.The imports correctly reference the required types from the server types file.
3-14
: Well-documented function overload for error responses.The JSDoc comments for the error response overload are clear and descriptive.
16-29
: Well-documented function overload for success responses.The JSDoc comments for the success response overload are clear and descriptive.
uno.config.ts (1)
13-14
: Formatting change looks goodThe removal of the trailing comma and addition of a newline at the end of the file are consistent with standard code formatting practices.
components/ui/progress/index.ts (1)
1-2
: Clean component export patternThis export follows a good pattern for a component library, making the Progress component easily importable throughout the application.
components/ui/skeleton/index.ts (1)
1-2
: Clean component export patternThis export follows the same consistent pattern as other UI components, making the Skeleton component easily importable throughout the application.
components/tabManager.vue (1)
33-40
: The tab UI implementation looks good!The component correctly implements a responsive tab interface with proper active tab styling. The responsive design hides text labels on mobile and shows them on larger screens.
package.json (2)
20-39
: Dependencies look appropriate for the project updates.The added dependencies align well with the UI overhaul, state management, and theming requirements described in the PR. The package versions appear to be current and compatible.
45-47
: Icon packages addition looks good.The added iconify packages for cryptocurrency and flag icons will enhance the UI capabilities of the application.
server/api/weather/sunset.post.ts (3)
5-27
: Excellent JSDoc documentation!The comprehensive documentation for this API endpoint clearly explains its purpose, parameters, return values, and potential error cases. This is a great practice for maintainability.
38-51
: Input validation is well implemented.The code thoroughly validates both the presence of required fields and their types, returning appropriate error responses when validation fails.
62-68
: Error handling is well implemented.The error handling distinguishes between Error objects and other error types, and customizes the error message based on the error content. This is a good practice.
components/deviceInfo.vue (1)
19-38
: Good implementation of device metrics display.The device information is well organized in a responsive grid layout with appropriate icons. The component effectively leverages the browser's device APIs through Vue composables.
components/ui/badge/Badge.vue (1)
1-28
: Well-structured component with good TypeScript usageThe Badge component is well-implemented with proper TypeScript type safety, clean prop handling with computed properties, and good separation of concerns. The component correctly delegates props and handles class combinations efficiently.
components/ui/badge/index.ts (1)
1-25
: Well-structured badge component with good variant definitionsThe badge implementation is well-structured with clean variant definitions using class-variance-authority. The component properly exports the Badge component, variant utility function, and TypeScript types for good type safety.
components/ui/progress/Progress.vue (4)
1-9
: Solid component setup with proper dependencies and type definitions.The component correctly imports necessary dependencies from reka-ui and sets up TypeScript types. The use of the utility function
cn
from lib/utils is a good practice for conditional class merging.
10-15
: Well-structured props definition with sensible defaults.The component properly extends ProgressRootProps while adding an optional class prop. Setting the default modelValue to 0 is appropriate for a progress component.
17-21
: Clean separation of class prop from other props.Good use of a computed property to extract the class prop from other props, preventing it from being passed directly to the ProgressRoot component.
24-34
: Proper styling and prop binding for the progress root.The component correctly binds delegated props and applies conditional class names using the cn utility.
assets/css/tailwind.css (3)
7-44
: Well-organized CSS theme variables mapping.The inline theme definition creates a clear mapping between semantic color variables and their implementation variables, which is excellent for maintainability.
46-80
: Comprehensive light theme color definitions using OKLCH color space.The root variables define a complete set of colors for the light theme using the modern OKLCH color space, which provides better perceptual uniformity across different displays.
82-115
: Well-defined dark theme with appropriate color contrast.The dark theme variables maintain proper contrast ratios while providing a cohesive dark experience. The OKLCH values appear to be carefully selected for consistency.
components/ui/button/index.ts (4)
1-4
: Clean import and export setup.The file correctly imports CVA and exports the Button component. This approach maintains separation of concerns between component implementation and styling configuration.
8-21
: Comprehensive button variants with appropriate styling.The button variants cover a wide range of use cases with appropriate styling for each. The use of semantic color variables from your theme ensures consistency across the application.
22-34
: Well-defined button sizes with responsive considerations.The size variants provide good flexibility with responsive adjustments for icons. The defaultVariants ensure consistent rendering when no specific variant is chosen.
36-36
: Proper type export for type-safe usage.Exporting the ButtonVariants type provides type safety when using the component, which is a good practice for maintainability.
export function getOrSetItem(key: string, value: unknown) { | ||
const db = localStorage; | ||
if(key && typeof key === "string") { | ||
if (db.getItem(key) === null) { | ||
db.setItem(key, JSON.stringify(value)); | ||
} else { | ||
const item = db.getItem(key); | ||
return item !== null ? JSON.parse(item) : null; | ||
} | ||
} | ||
return value; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix logic and indentation in getOrSetItem function.
There are issues with the logic and indentation in this function:
- The function returns
value
if the key is valid but not in storage, instead of setting it and returning it - The indentation is inconsistent (line 17 is over-indented)
export function getOrSetItem(key: string, value: unknown) {
const db = localStorage;
if(key && typeof key === "string") {
if (db.getItem(key) === null) {
db.setItem(key, JSON.stringify(value));
+ return value;
} else {
const item = db.getItem(key);
return item !== null ? JSON.parse(item) : null;
}
- }
+ }
return value;
}
📝 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.
export function getOrSetItem(key: string, value: unknown) { | |
const db = localStorage; | |
if(key && typeof key === "string") { | |
if (db.getItem(key) === null) { | |
db.setItem(key, JSON.stringify(value)); | |
} else { | |
const item = db.getItem(key); | |
return item !== null ? JSON.parse(item) : null; | |
} | |
} | |
return value; | |
} | |
export function getOrSetItem(key: string, value: unknown) { | |
const db = localStorage; | |
if (key && typeof key === "string") { | |
if (db.getItem(key) === null) { | |
db.setItem(key, JSON.stringify(value)); | |
return value; | |
} else { | |
const item = db.getItem(key); | |
return item !== null ? JSON.parse(item) : null; | |
} | |
} | |
return value; | |
} |
font: { | ||
adblox: 'Adblox', | ||
nixav: 'UT Defghio Nixav', | ||
'acro-mono': 'AcroMono', | ||
panton: 'panton', | ||
uni: 'Uni Sans Heavy', | ||
espial: 'EspialRegular15-6Y08Y', | ||
action: 'Action_Man', | ||
fifa: 'fifawelcome', | ||
intro: 'Intro', | ||
japanese: 'Japanese', | ||
product: "Product Sans", | ||
sans: "Product Sans" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Enhanced font configuration approach
The new font
property provides more flexibility than the previous fontFamily
property by allowing multiple named font configurations that can be used across different UI components.
A few things to consider:
- Verify that all these fonts are properly loaded and available in your application
- Consider adding comments for non-standard font names like 'EspialRegular15-6Y08Y'
- Note that both 'product' and 'sans' map to the same font value - is this intentional?
🏁 Script executed:
#!/bin/bash
# Check if font files exist for the fonts defined in the theme
echo "Checking for font files in the project..."
fd -t f -e ttf -e otf -e woff -e woff2
Length of output: 2856
Fonts confirmed; minor tweaks recommended
All entries in the font
map have matching files under public/fonts/…
, so your font assets are properly loaded.
A couple of small improvements to consider:
- Add an inline comment explaining non‑standard identifiers like
EspialRegular15-6Y08Y
for future maintainers. - Verify whether
product
andsans
both mapping to"Product Sans"
is an intentional alias or a typo.
if (response.ok && response.data) { | ||
isLoading.value = false | ||
data.value = response.data | ||
console.log(data) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling for API responses
The current implementation only checks if response.ok
is true but doesn't handle API errors. Also, there's a console.log statement that should be removed.
if (response.ok && response.data) {
isLoading.value = false
data.value = response.data
- console.log(data)
+ } else {
+ errorMessage.value = response.error || 'Failed to fetch time metrics'
+ isLoading.value = false
}
📝 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.
if (response.ok && response.data) { | |
isLoading.value = false | |
data.value = response.data | |
console.log(data) | |
} | |
if (response.ok && response.data) { | |
isLoading.value = false | |
data.value = response.data | |
} else { | |
errorMessage.value = response.error || 'Failed to fetch time metrics' | |
isLoading.value = false | |
} |
const refreshContent = async () => { | ||
isLoading.value = true | ||
try { | ||
const reqBody = [] | ||
for (const item of instruments) { | ||
reqBody.push({ | ||
pair: item.name, | ||
base: item.name.split('/')[0], | ||
quote: item.name.split('/')[1] | ||
}); | ||
} | ||
await ratesStore.fetchRates(reqBody) | ||
isLoading.value = false; | ||
|
||
} catch (error) { | ||
console.log(error) | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve refreshContent error handling and use reusable function
Use the extracted function for building the request body and improve error handling.
const refreshContent = async () => {
isLoading.value = true
try {
- const reqBody = []
- for (const item of instruments) {
- reqBody.push({
- pair: item.name,
- base: item.name.split('/')[0],
- quote: item.name.split('/')[1]
- });
- }
- await ratesStore.fetchRates(reqBody)
+ await ratesStore.fetchRates(buildRequestBody())
isLoading.value = false;
} catch (error) {
- console.log(error)
+ console.error('Error refreshing rates:', error)
+ isLoading.value = false
+ // Consider showing a user-facing error message
}
}
📝 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.
const refreshContent = async () => { | |
isLoading.value = true | |
try { | |
const reqBody = [] | |
for (const item of instruments) { | |
reqBody.push({ | |
pair: item.name, | |
base: item.name.split('/')[0], | |
quote: item.name.split('/')[1] | |
}); | |
} | |
await ratesStore.fetchRates(reqBody) | |
isLoading.value = false; | |
} catch (error) { | |
console.log(error) | |
} | |
} | |
const refreshContent = async () => { | |
isLoading.value = true | |
try { | |
await ratesStore.fetchRates(buildRequestBody()) | |
isLoading.value = false; | |
} catch (error) { | |
console.error('Error refreshing rates:', error) | |
isLoading.value = false | |
// Consider showing a user-facing error message | |
} | |
} |
try { | ||
const reqBody = [] | ||
for (const item of instruments) { | ||
reqBody.push({ | ||
pair: item.name, | ||
base: item.name.split('/')[0], | ||
quote: item.name.split('/')[1] | ||
}); | ||
} | ||
await ratesStore.fetchRatesFromStorage(reqBody) | ||
isLoading.value = false; | ||
|
||
} catch (error) { | ||
console.log(error) | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor duplicate code into a reusable function
The code to construct the request body is duplicated in both onMounted
and refreshContent
methods. Extract this into a separate function.
+const buildRequestBody = () => {
+ const reqBody = []
+ for (const item of instruments) {
+ reqBody.push({
+ pair: item.name,
+ base: item.name.split('/')[0],
+ quote: item.name.split('/')[1]
+ });
+ }
+ return reqBody
+}
+
onMounted(async () => {
try {
- const reqBody = []
- for (const item of instruments) {
- reqBody.push({
- pair: item.name,
- base: item.name.split('/')[0],
- quote: item.name.split('/')[1]
- });
- }
- await ratesStore.fetchRatesFromStorage(reqBody)
+ await ratesStore.fetchRatesFromStorage(buildRequestBody())
isLoading.value = false;
} catch (error) {
console.log(error)
}
})
📝 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.
try { | |
const reqBody = [] | |
for (const item of instruments) { | |
reqBody.push({ | |
pair: item.name, | |
base: item.name.split('/')[0], | |
quote: item.name.split('/')[1] | |
}); | |
} | |
await ratesStore.fetchRatesFromStorage(reqBody) | |
isLoading.value = false; | |
} catch (error) { | |
console.log(error) | |
} | |
const buildRequestBody = () => { | |
const reqBody = [] | |
for (const item of instruments) { | |
reqBody.push({ | |
pair: item.name, | |
base: item.name.split('/')[0], | |
quote: item.name.split('/')[1] | |
}) | |
} | |
return reqBody | |
} | |
onMounted(async () => { | |
try { | |
await ratesStore.fetchRatesFromStorage(buildRequestBody()) | |
isLoading.value = false | |
} catch (error) { | |
console.log(error) | |
} | |
}) |
<div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1" | ||
v-for="item, k in instruments"> | ||
<h2 class="font-extrabold mb1 text-2xl">${{ | ||
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }} | ||
</h2> | ||
<div class="flex items-center gap-x-2"> | ||
<div class=" rounded-full grid place-items-center overflow-hidden"> | ||
<Icon :name="item.icon" size="26" /> | ||
</div> | ||
<p class="text-base">{{ item.name }}</p> | ||
</div> | ||
<!-- <div class="flex items-end gap-x-1"> | ||
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" /> | ||
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" /> | ||
<p class="text-lg mt2 font-bold" | ||
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{ | ||
item.change | ||
}}%</p> | ||
|
||
</div> --> | ||
|
||
</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve error handling and rate comparison safety
The component doesn't handle cases where the API returns fewer rates than expected and uses index-based access which could be fragile.
- <div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
+ <div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1"
v-for="item, k in instruments">
+ <template v-if="k < ratesStore.rates.length && ratesStore.rates[k]">
<h2 class="font-extrabold mb1 text-2xl">${{
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }}
</h2>
<div class="flex items-center gap-x-2">
<div class=" rounded-full grid place-items-center overflow-hidden">
<Icon :name="item.icon" size="26" />
</div>
<p class="text-base">{{ item.name }}</p>
</div>
+ </template>
+ <div v-else class="text-red-500">
+ Failed to load rate for {{ item.name }}
+ </div>
<!-- <div class="flex items-end gap-x-1">
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" />
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" />
<p class="text-lg mt2 font-bold"
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{
item.change
}}%</p>
</div> -->
</div>
📝 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.
<div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1" | |
v-for="item, k in instruments"> | |
<h2 class="font-extrabold mb1 text-2xl">${{ | |
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }} | |
</h2> | |
<div class="flex items-center gap-x-2"> | |
<div class=" rounded-full grid place-items-center overflow-hidden"> | |
<Icon :name="item.icon" size="26" /> | |
</div> | |
<p class="text-base">{{ item.name }}</p> | |
</div> | |
<!-- <div class="flex items-end gap-x-1"> | |
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" /> | |
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" /> | |
<p class="text-lg mt2 font-bold" | |
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{ | |
item.change | |
}}%</p> | |
</div> --> | |
</div> | |
<div class="rounded-2xl p3 bg-stone-100" v-if="!isLoading && ratesStore.rates.length >= 1" | |
v-for="item, k in instruments"> | |
<template v-if="k < ratesStore.rates.length && ratesStore.rates[k]"> | |
<h2 class="font-extrabold mb1 text-2xl">${{ | |
Intl.NumberFormat('us').format(ratesStore.rates[k].rate).toString() }} | |
</h2> | |
<div class="flex items-center gap-x-2"> | |
<div class=" rounded-full grid place-items-center overflow-hidden"> | |
<Icon :name="item.icon" size="26" /> | |
</div> | |
<p class="text-base">{{ item.name }}</p> | |
</div> | |
</template> | |
<div v-else class="text-red-500"> | |
Failed to load rate for {{ item.name }} | |
</div> | |
<!-- <div class="flex items-end gap-x-1"> | |
<Icon name="solar:alt-arrow-down-bold" class="text-red-400" v-if="item.change < 0" size="26" /> | |
<Icon name="solar:alt-arrow-up-bold" class="text-green-400" v-if="item.change > 0" size="26" /> | |
<p class="text-lg mt2 font-bold" | |
:class="{ '!text-green-500': item.change > 0, '!text-red-500': item.change < 0 }">{{ | |
item.change | |
}}%</p> | |
</div> --> | |
</div> |
export type CurrencyBeaconResponse = { | ||
meta: { | ||
code: number; | ||
disclaimer: string; | ||
}; | ||
response: { | ||
date: string; | ||
base: string; | ||
rates: { | ||
[quote: string]: number; | ||
}; | ||
}; | ||
date: string; | ||
base: string; | ||
rates: { | ||
[quote: string]: number; | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix duplicate fields in CurrencyBeaconResponse type
There are duplicate fields (date
, base
, rates
) at both the root level and nested under response
. This could lead to confusion when using this type.
export type CurrencyBeaconResponse = {
meta: {
code: number;
disclaimer: string;
};
response: {
date: string;
base: string;
rates: {
[quote: string]: number;
};
};
- date: string;
- base: string;
- rates: {
- [quote: string]: number;
- };
};
📝 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.
export type CurrencyBeaconResponse = { | |
meta: { | |
code: number; | |
disclaimer: string; | |
}; | |
response: { | |
date: string; | |
base: string; | |
rates: { | |
[quote: string]: number; | |
}; | |
}; | |
date: string; | |
base: string; | |
rates: { | |
[quote: string]: number; | |
}; | |
}; | |
export type CurrencyBeaconResponse = { | |
meta: { | |
code: number; | |
disclaimer: string; | |
}; | |
response: { | |
date: string; | |
base: string; | |
rates: { | |
[quote: string]: number; | |
}; | |
}; | |
}; |
Overview section 70% complete. This PR is to debug a bug with the timeMetrics component.
Summary by Sourcery
Enhance the application's configuration and add new components for time metrics, device information, and data fetching
New Features:
Enhancements:
Build:
CI:
Summary by CodeRabbit
New Features
Bug Fixes
Chores
Documentation