diff --git a/app/Clients/ZonneplanPriceClient.php b/app/Clients/ZonneplanPriceClient.php index 9e9d888..837ca5f 100644 --- a/app/Clients/ZonneplanPriceClient.php +++ b/app/Clients/ZonneplanPriceClient.php @@ -72,7 +72,7 @@ class ZonneplanPriceClient implements PriceClient ]); throw new Exception( - "Kon energieprijzen niet ophalen voor {$path}", + __('errors.client_error'), previous: $e, ); } diff --git a/lang/en/auth.php b/lang/en/auth.php new file mode 100644 index 0000000..6598e2c --- /dev/null +++ b/lang/en/auth.php @@ -0,0 +1,20 @@ + 'These credentials do not match our records.', + 'password' => 'The provided password is incorrect.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + +]; diff --git a/lang/en/pagination.php b/lang/en/pagination.php new file mode 100644 index 0000000..d481411 --- /dev/null +++ b/lang/en/pagination.php @@ -0,0 +1,19 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/en/passwords.php b/lang/en/passwords.php new file mode 100644 index 0000000..fad3a7d --- /dev/null +++ b/lang/en/passwords.php @@ -0,0 +1,22 @@ + 'Your password has been reset.', + 'sent' => 'We have emailed your password reset link.', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => "We can't find a user with that email address.", + +]; diff --git a/lang/en/validation.php b/lang/en/validation.php new file mode 100644 index 0000000..185720d --- /dev/null +++ b/lang/en/validation.php @@ -0,0 +1,201 @@ + 'The :attribute field must be accepted.', + 'accepted_if' => 'The :attribute field must be accepted when :other is :value.', + 'active_url' => 'The :attribute field must be a valid URL.', + 'after' => 'The :attribute field must be a date after :date.', + 'after_or_equal' => 'The :attribute field must be a date after or equal to :date.', + 'alpha' => 'The :attribute field must only contain letters.', + 'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.', + 'alpha_num' => 'The :attribute field must only contain letters and numbers.', + 'any_of' => 'The :attribute field is invalid.', + 'array' => 'The :attribute field must be an array.', + 'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.', + 'base64' => 'The :attribute field must be a valid Base64 string.', + 'before' => 'The :attribute field must be a date before :date.', + 'before_or_equal' => 'The :attribute field must be a date before or equal to :date.', + 'between' => [ + 'array' => 'The :attribute field must have between :min and :max items.', + 'file' => 'The :attribute field must be between :min and :max kilobytes.', + 'numeric' => 'The :attribute field must be between :min and :max.', + 'string' => 'The :attribute field must be between :min and :max characters.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'can' => 'The :attribute field contains an unauthorized value.', + 'confirmed' => 'The :attribute field confirmation does not match.', + 'contains' => 'The :attribute field is missing a required value.', + 'current_password' => 'The password is incorrect.', + 'date' => 'The :attribute field must be a valid date.', + 'date_equals' => 'The :attribute field must be a date equal to :date.', + 'date_format' => 'The :attribute field must match the format :format.', + 'decimal' => 'The :attribute field must have :decimal decimal places.', + 'declined' => 'The :attribute field must be declined.', + 'declined_if' => 'The :attribute field must be declined when :other is :value.', + 'different' => 'The :attribute field and :other must be different.', + 'digits' => 'The :attribute field must be :digits digits.', + 'digits_between' => 'The :attribute field must be between :min and :max digits.', + 'dimensions' => 'The :attribute field has invalid image dimensions.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'doesnt_contain' => 'The :attribute field must not contain any of the following: :values.', + 'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.', + 'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.', + 'email' => 'The :attribute field must be a valid email address.', + 'encoding' => 'The :attribute field must be encoded in :encoding.', + 'ends_with' => 'The :attribute field must end with one of the following: :values.', + 'enum' => 'The selected :attribute is invalid.', + 'exists' => 'The selected :attribute is invalid.', + 'extensions' => 'The :attribute field must have one of the following extensions: :values.', + 'file' => 'The :attribute field must be a file.', + 'filled' => 'The :attribute field must have a value.', + 'gt' => [ + 'array' => 'The :attribute field must have more than :value items.', + 'file' => 'The :attribute field must be greater than :value kilobytes.', + 'numeric' => 'The :attribute field must be greater than :value.', + 'string' => 'The :attribute field must be greater than :value characters.', + ], + 'gte' => [ + 'array' => 'The :attribute field must have :value items or more.', + 'file' => 'The :attribute field must be greater than or equal to :value kilobytes.', + 'numeric' => 'The :attribute field must be greater than or equal to :value.', + 'string' => 'The :attribute field must be greater than or equal to :value characters.', + ], + 'hex_color' => 'The :attribute field must be a valid hexadecimal color.', + 'image' => 'The :attribute field must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'in_array' => 'The :attribute field must exist in :other.', + 'in_array_keys' => 'The :attribute field must contain at least one of the following keys: :values.', + 'integer' => 'The :attribute field must be an integer.', + 'ip' => 'The :attribute field must be a valid IP address.', + 'ipv4' => 'The :attribute field must be a valid IPv4 address.', + 'ipv6' => 'The :attribute field must be a valid IPv6 address.', + 'json' => 'The :attribute field must be a valid JSON string.', + 'list' => 'The :attribute field must be a list.', + 'lowercase' => 'The :attribute field must be lowercase.', + 'lt' => [ + 'array' => 'The :attribute field must have less than :value items.', + 'file' => 'The :attribute field must be less than :value kilobytes.', + 'numeric' => 'The :attribute field must be less than :value.', + 'string' => 'The :attribute field must be less than :value characters.', + ], + 'lte' => [ + 'array' => 'The :attribute field must not have more than :value items.', + 'file' => 'The :attribute field must be less than or equal to :value kilobytes.', + 'numeric' => 'The :attribute field must be less than or equal to :value.', + 'string' => 'The :attribute field must be less than or equal to :value characters.', + ], + 'mac_address' => 'The :attribute field must be a valid MAC address.', + 'max' => [ + 'array' => 'The :attribute field must not have more than :max items.', + 'file' => 'The :attribute field must not be greater than :max kilobytes.', + 'numeric' => 'The :attribute field must not be greater than :max.', + 'string' => 'The :attribute field must not be greater than :max characters.', + ], + 'max_digits' => 'The :attribute field must not have more than :max digits.', + 'mimes' => 'The :attribute field must be a file of type: :values.', + 'mimetypes' => 'The :attribute field must be a file of type: :values.', + 'min' => [ + 'array' => 'The :attribute field must have at least :min items.', + 'file' => 'The :attribute field must be at least :min kilobytes.', + 'numeric' => 'The :attribute field must be at least :min.', + 'string' => 'The :attribute field must be at least :min characters.', + ], + 'min_digits' => 'The :attribute field must have at least :min digits.', + 'missing' => 'The :attribute field must be missing.', + 'missing_if' => 'The :attribute field must be missing when :other is :value.', + 'missing_unless' => 'The :attribute field must be missing unless :other is :value.', + 'missing_with' => 'The :attribute field must be missing when :values is present.', + 'missing_with_all' => 'The :attribute field must be missing when :values are present.', + 'multiple_of' => 'The :attribute field must be a multiple of :value.', + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute field format is invalid.', + 'numeric' => 'The :attribute field must be a number.', + 'password' => [ + 'letters' => 'The :attribute field must contain at least one letter.', + 'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.', + 'numbers' => 'The :attribute field must contain at least one number.', + 'symbols' => 'The :attribute field must contain at least one symbol.', + 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', + ], + 'present' => 'The :attribute field must be present.', + 'present_if' => 'The :attribute field must be present when :other is :value.', + 'present_unless' => 'The :attribute field must be present unless :other is :value.', + 'present_with' => 'The :attribute field must be present when :values is present.', + 'present_with_all' => 'The :attribute field must be present when :values are present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_if_accepted' => 'The :attribute field is prohibited when :other is accepted.', + 'prohibited_if_declined' => 'The :attribute field is prohibited when :other is declined.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', + 'regex' => 'The :attribute field format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', + 'required_if_declined' => 'The :attribute field is required when :other is declined.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values are present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute field must match :other.', + 'size' => [ + 'array' => 'The :attribute field must contain :size items.', + 'file' => 'The :attribute field must be :size kilobytes.', + 'numeric' => 'The :attribute field must be :size.', + 'string' => 'The :attribute field must be :size characters.', + ], + 'starts_with' => 'The :attribute field must start with one of the following: :values.', + 'string' => 'The :attribute field must be a string.', + 'timezone' => 'The :attribute field must be a valid timezone.', + 'unique' => 'The :attribute has already been taken.', + 'uploaded' => 'The :attribute failed to upload.', + 'uppercase' => 'The :attribute field must be uppercase.', + 'url' => 'The :attribute field must be a valid URL.', + 'ulid' => 'The :attribute field must be a valid ULID.', + 'uuid' => 'The :attribute field must be a valid UUID.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [], + +]; diff --git a/lang/nl/errors.php b/lang/nl/errors.php new file mode 100644 index 0000000..16bec2e --- /dev/null +++ b/lang/nl/errors.php @@ -0,0 +1,5 @@ + 'Kon energieprijzen niet ophalen' +]; diff --git a/resources/js/components/Error.vue b/resources/js/components/Error.vue new file mode 100644 index 0000000..b3e373d --- /dev/null +++ b/resources/js/components/Error.vue @@ -0,0 +1,11 @@ + + + diff --git a/resources/js/features/dashboard/components/Price.vue b/resources/js/features/dashboard/components/Price.vue index d532951..3bb69ca 100644 --- a/resources/js/features/dashboard/components/Price.vue +++ b/resources/js/features/dashboard/components/Price.vue @@ -3,7 +3,7 @@ import moment from 'moment'; defineProps<{ label: string; - price: number | null | undefined; + price: number; time: moment.Moment | string; }>(); diff --git a/resources/js/features/dashboard/pages/Index.vue b/resources/js/features/dashboard/pages/Index.vue index 135df27..7243444 100644 --- a/resources/js/features/dashboard/pages/Index.vue +++ b/resources/js/features/dashboard/pages/Index.vue @@ -4,6 +4,7 @@ import moment from 'moment'; import { onMounted } from 'vue'; import Price from '../components/Price.vue'; import useDashboardViewModel from '../viewmodels/DashboardViewModel'; +import Error from '@/components/Error.vue'; const model = useDashboardViewModel(); @@ -16,12 +17,15 @@ onMounted(() => {

{{ t('dashboard.title') }}

-
+

{{ t('dashboard.electricity') }}

+

{{ t('dashboard.gas') }}

diff --git a/resources/js/features/dashboard/viewmodels/DashboardViewModel.ts b/resources/js/features/dashboard/viewmodels/DashboardViewModel.ts index 5108f14..ee85494 100644 --- a/resources/js/features/dashboard/viewmodels/DashboardViewModel.ts +++ b/resources/js/features/dashboard/viewmodels/DashboardViewModel.ts @@ -4,6 +4,7 @@ import DashboardData = App.Features.Dashboard.Data.DashboardData; export default function useDashboardViewModel() { const isLoading = ref(false); + const error = ref(); const dashboardData = ref(); function getDashboardData() { @@ -15,8 +16,11 @@ export default function useDashboardViewModel() { .then((res) => { dashboardData.value = res.data; }) + .catch((err) => { + error.value = err.response.data.message; + }) .finally(() => (isLoading.value = false)); } - return { isLoading, dashboardData, getDashboardData }; + return { isLoading, dashboardData, error, getDashboardData }; } diff --git a/resources/js/features/history/components/Chart.vue b/resources/js/features/history/components/Chart.vue index 29b2f87..c09101f 100644 --- a/resources/js/features/history/components/Chart.vue +++ b/resources/js/features/history/components/Chart.vue @@ -24,7 +24,7 @@ ChartJS.register( export interface PricePoint { label: string; - value: number; + value: number | null; } const props = defineProps<{ diff --git a/resources/js/lang/translations/index.ts b/resources/js/lang/translations/index.ts index 21c29f5..28a54c9 100644 --- a/resources/js/lang/translations/index.ts +++ b/resources/js/lang/translations/index.ts @@ -1,5 +1,5 @@ -import nl from "./nl"; +import nl from './nl'; export default { - nl -} + nl: { translation: nl }, +}; diff --git a/resources/js/lang/translations/nl/dashboard.json b/resources/js/lang/translations/nl/dashboard.json index cc61669..1b3e380 100644 --- a/resources/js/lang/translations/nl/dashboard.json +++ b/resources/js/lang/translations/nl/dashboard.json @@ -1,3 +1,12 @@ { - "title": "Overzicht" + "title": "Overzicht", + "electricity": "Elektriciteit", + "gas": "Gas", + "current_electricity_price": "Actuele elektriciteitsprijs", + "cheapest_electricity_price": "Goedkoopste elektriciteitsprijs", + "durable_electricity_price": "Duurzaamste elektriciteitsprijs", + "priciest_electricity_price": "Duurste elektriciteitsprijs", + "current_gas_price": "Actuele gasprijs", + "cheapest_gas_price": "Goedkoopste gasprijs", + "priciest_gas_price": "Duurste gasprijs" } diff --git a/resources/js/lang/translations/nl/history.json b/resources/js/lang/translations/nl/history.json new file mode 100644 index 0000000..29bf569 --- /dev/null +++ b/resources/js/lang/translations/nl/history.json @@ -0,0 +1,3 @@ +{ + "title": "Prijshistorie" +} diff --git a/resources/js/lang/translations/nl/index.ts b/resources/js/lang/translations/nl/index.ts index f483f3c..0b04b45 100644 --- a/resources/js/lang/translations/nl/index.ts +++ b/resources/js/lang/translations/nl/index.ts @@ -1,5 +1,7 @@ import dashboard from './dashboard.json'; +import history from './history.json'; export default { dashboard, + history, }; diff --git a/resources/js/layouts/Layout.vue b/resources/js/layouts/Layout.vue index bbd2106..cb12621 100644 --- a/resources/js/layouts/Layout.vue +++ b/resources/js/layouts/Layout.vue @@ -2,17 +2,33 @@ import { t } from 'i18next'; import dashboard from '@/routes/dashboard'; import history from '@/routes/history'; +import { Link, router, usePage } from '@inertiajs/vue3'; +import { computed } from 'vue'; + +const page = usePage(); +const currentUrl = computed(() => page.url); + +const navItems = [ + { href: dashboard.index().url, label: 'dashboard.title' }, + { href: history.index().url, label: 'history.title' }, +];