Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
629bf20
feat: migrate frontend from Ziggy to Laravel Wayfinder
herpaderpaldent May 6, 2026
35263a8
chore: upgrade to Vite 6 + ESM-first config stack
herpaderpaldent May 7, 2026
19b889f
fix: expose Wayfinder route functions to Vue templates
herpaderpaldent May 7, 2026
52d7c23
fix: auto-publish on monorepo dev start + remove orphaned @routes dir…
herpaderpaldent May 7, 2026
247c763
fix: add favicon link to blade template
herpaderpaldent May 7, 2026
b102a07
feat: upgrade Inertia to v3, replace custom Handler with handleExcept…
herpaderpaldent May 8, 2026
f73933a
chore: remove unused Exception/Handler.php (replaced by handleExcepti…
herpaderpaldent May 8, 2026
6649f15
fix: expose Wayfinder route fns via setup() in Options API components
herpaderpaldent May 8, 2026
2f8d105
fix: merge duplicate setup() in EditSettings, add build:false to vite…
herpaderpaldent May 8, 2026
7eb3c3d
fix: restore Ignition debug page for 500/503 in local environment
herpaderpaldent May 8, 2026
580554c
fix: restore Ignition for initial-page 500s in local, keep Error.vue …
herpaderpaldent May 8, 2026
0ad490b
fix: replace Ziggy route() with Wayfinder-compatible item.uri in Dark…
herpaderpaldent May 8, 2026
d5aec3b
feat: complete Ziggy → Wayfinder migration (remove all route() calls)
herpaderpaldent May 8, 2026
61b8651
refactor: replace axios with native fetch (apiFetch helper)
herpaderpaldent May 8, 2026
8848246
fix: restore vendor/I18n.js (deleted by rsync) and unignore resources…
herpaderpaldent May 8, 2026
bca9664
fix: getCharacterIds() now returns plain integer IDs instead of Eloqu…
herpaderpaldent May 8, 2026
f047c85
fix: replace layout:null with TransparentLayout to fix Inertia v3 par…
herpaderpaldent May 8, 2026
2fe9237
fix: return headers from setup() in MobileMailList and DesktopMailList
herpaderpaldent May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ node_modules/
.idea

vendor/
!resources/js/vendor/

public/css/

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"seatplus/auth": "^4.0.5",
"conedevelopment/i18n": "^1.1",
"doctrine/dbal": "^3.0",
"inertiajs/inertia-laravel": "^2.0",
"inertiajs/inertia-laravel": "^3.0",
"laravel/wayfinder": "^0.1.16"
},
"require-dev": {
Expand Down
2,992 changes: 853 additions & 2,139 deletions package-lock.json

Large diffs are not rendered by default.

33 changes: 13 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,43 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"prod": "node --max-old-space-size=1024 ./node_modules/vite/bin/vite build",
"debug": "node --max-old-space-size=1024 ./node_modules/vite/bin/vite build --mode debug"
"prod": "node --max-old-space-size=1024 ./node_modules/vite/bin/vite.js build",
"debug": "node --max-old-space-size=1024 ./node_modules/vite/bin/vite.js build --mode debug"
},
"devDependencies": {
"@eslint/js": "^9.4.0",
"vite-plugin-run": "^0.5.1",
"@tailwindcss/aspect-ratio": "^0.4.0",
"@tailwindcss/forms": "^0.5.2",
"@tailwindcss/typography": "^0.5.4",
"@vitejs/plugin-vue": "^4.0",
"@vue/compiler-sfc": "^3.0.7",
"acorn": "^8.0",
"@vitejs/plugin-vue": "^5.2.0",
"autoprefixer": "^10.4.8",
"eslint": "^9.4.0",
"eslint-plugin-vue": "^9.26.0",
"globals": "^15.3.0",
"laravel-vite-plugin": "^0.8.1",
"laravel-vite-plugin": "^1.3.0",
"postcss": "^8.4.16",
"resolve-url-loader": "^5.0.0",
"rollup-plugin-copy": "^3.4.0",
"sass": "^1.32.5",
"sass-loader": "^14.2.1",
"tailwindcss": "^3.1.8",
"vite": "^4.0",
"vue": "^3.0.0-0",
"vue-loader": "^17.0.0",
"vue-template-compiler": "^2.6.12"
"tailwindcss": "^3.4.0",
"vite": "^6.0.0",
"vite-plugin-run": "^0.8.0"
},
"dependencies": {
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@headlessui/vue": "^1.0.0",
"@heroicons/vue": "^2.0.11",
"@inertiajs/vue3": "^1.0.0",
"@headlessui/vue": "^1.7.0",
"@heroicons/vue": "^2.1.0",
"@inertiajs/vue3": "^3.0",
"@popperjs/core": "^2.6.0",
"@seregpie/vue-storage": "^1.1.1",
"@vueuse/core": "^10.0.2",
"@vueuse/core": "^11.0.0",
"chart.js": "^4.0.0",
"dayjs": "^1.10.4",
"lodash": "^4.17.20",
"metric-prefix": "^0.9.0",
"postcss-import": "^16.1.0",
"uuid": "^9.0.0",
"vue": "^3.5.0",
"vue-chartjs": "^5.0.0"
}
}
4 changes: 2 additions & 2 deletions postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
}
4 changes: 4 additions & 0 deletions resources/js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/routes/
/actions/
/wayfinder.ts
/wayfinder/
57 changes: 57 additions & 0 deletions resources/js/Functions/apiFetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* Thin fetch() wrapper that mirrors the behaviour of the previous axios setup:
* - Reads the XSRF-TOKEN cookie and forwards it as X-XSRF-TOKEN (Laravel CSRF)
* - Sets X-Requested-With: XMLHttpRequest so Laravel treats the request as AJAX
* - Accepts query params as a plain object (appended to the URL)
* - Returns the parsed JSON body directly (like axios `.data`)
* - Throws on non-2xx responses (like axios does by default)
* - Accepts an AbortSignal for cancellation
*/

function getCsrfToken() {
const match = document.cookie.match(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/)
return match ? decodeURIComponent(match[1]) : null
}

/**
* @param {string} url
* @param {Object} [options]
* @param {string} [options.method='GET']
* @param {Object|null} [options.params] - query string params
* @param {Object|null} [options.data] - JSON body (for POST/PUT/PATCH)
* @param {AbortSignal} [options.signal]
* @returns {Promise<any>} parsed JSON response body
*/
export async function apiFetch(url, { method = 'GET', params = null, data = null, signal = null } = {}) {
if (params && Object.keys(params).length) {
const qs = new URLSearchParams(
Object.entries(params).filter(([, v]) => v !== null && v !== undefined)
).toString()
url = url + (url.includes('?') ? '&' : '?') + qs
}

const headers = {
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
}

const csrf = getCsrfToken()
if (csrf) headers['X-XSRF-TOKEN'] = csrf

const fetchOptions = { method, headers }
if (signal) fetchOptions.signal = signal
if (data && Object.keys(data).length) {
headers['Content-Type'] = 'application/json'
fetchOptions.body = JSON.stringify(data)
}

const response = await fetch(url, fetchOptions)

if (!response.ok) {
const err = new Error(`HTTP ${response.status}: ${response.statusText}`)
err.response = response
throw err
}

return response.json()
}
11 changes: 6 additions & 5 deletions resources/js/Functions/useGetPrice.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {ls} from './useLocalStorage'
import axios from "axios";
import { prices as marketPrices } from '@/routes/get/markets'
import { apiFetch } from "@/Functions/apiFetch";

export function useGetPrice(type_id) {

Expand All @@ -11,10 +12,10 @@ export function useGetPrice(type_id) {
if(prices)
return prices

axios.get(route('get.markets.prices'))
.then(response => {
ls.set('markets.prices', response.data, 86400000) // 24hrs
return response.data
apiFetch(marketPrices().url)
.then(data => {
ls.set('markets.prices', data, 86400000) // 24hrs
return data
})
}

Expand Down
5 changes: 2 additions & 3 deletions resources/js/Functions/useHydrateQueryParameters.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@

export function useHydrateQueryParameters(params = {}) {

const queryParameters = Object.fromEntries(new URLSearchParams(window.location.search))

const queryParameters = route().params

return _.merge(params, queryParameters, )
return _.merge(params, queryParameters)

}
29 changes: 15 additions & 14 deletions resources/js/Functions/useInfinityScrolling.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import { onMounted, onUnmounted, ref } from "vue";
import { apiFetch } from "@/Functions/apiFetch";

export function useInfinityScrolling(routeName, params, method = 'GET') {
export function useInfinityScrolling(url, method = 'GET', postData = null) {

const url = ref(route(routeName,params))
const currentUrl = ref(url)
const scrollComponent = ref(null)
const result = ref([])
const isLoading = ref(false)
const isComplete = ref(false)
const isVisible = ref(null)

const source = axios.CancelToken.source()
let abortController = null

const fetchData = function () {

if(isLoading.value || isComplete.value || _.isNil(url.value))
if(isLoading.value || isComplete.value || _.isNil(currentUrl.value))
return

const timeout = setTimeout(() => isLoading.value = true, 250)

axios({
method: method,
url: url.value,
data: method === 'POST' ? params : null,
cancelToken: source.token,
params: params
abortController = new AbortController()

apiFetch(currentUrl.value, {
method,
data: method === 'POST' ? postData : null,
signal: abortController.signal,
})
.then(response => {

Expand All @@ -33,11 +34,11 @@ export function useInfinityScrolling(routeName, params, method = 'GET') {
isComplete.value = true;
}

result.value.push(...response.data.data);
url.value = response.data.links.next;
result.value.push(...response.data);
currentUrl.value = response.links.next;
})
.catch(error => {
console.log(error);
if (error.name !== 'AbortError') console.log(error)
}).finally(() => {
isLoading.value = false;
});
Expand Down Expand Up @@ -68,7 +69,7 @@ export function useInfinityScrolling(routeName, params, method = 'GET') {

onUnmounted(() => {
observer.disconnect()
source.cancel()
if (abortController) abortController.abort()
})

return {
Expand Down
87 changes: 42 additions & 45 deletions resources/js/Functions/useLoadCompleteResource.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,66 @@
import {computed, onBeforeMount, onBeforeUnmount, ref} from "vue";
import {useHydrateQueryParameters} from "./useHydrateQueryParameters";
import {onBeforeMount, onBeforeUnmount, ref} from "vue";
import { apiFetch } from "@/Functions/apiFetch";

export function useLoadCompleteResource(routeName, params, formData = {}) {
export function useLoadCompleteResource(url, formData = {}) {

const url = ref(route(routeName,useHydrateQueryParameters(params)))
const requestUrl = ref(url)
const results = ref([])
const isComplete = ref(true)

const method = computed(() => _.isEmpty(formData) ? 'get' : 'post')
const cleanFormData = computed(() => _.omitBy(formData, _.isNil))
const method = _.isEmpty(formData) ? 'GET' : 'POST'
const cleanFormData = _.omitBy(formData, _.isNil)

const CancelToken = axios.CancelToken;
let cancelTokens = [];
let abortController = null

const fetchData = async () => {

abortController = new AbortController()
const signal = abortController.signal

let last_page = 1

await axios.request({
method: method.value,
url: url.value,
params: { page: 1 },
data: cleanFormData.value
})
.then(response => {
try {
const response = await apiFetch(requestUrl.value, {
method,
params: { page: 1 },
data: cleanFormData,
signal,
})

last_page = _.get(response, 'data.last_page', _.get(response, 'data.meta.last_page'))
last_page = _.get(response, 'last_page', _.get(response, 'meta.last_page', 1))

if (response.data.data.length) {
results.value.push(...response.data.data);
}
})
.catch(error => console.log(error))

const axiosRequests = []

for(let i=2; i<= last_page; i++) {
axiosRequests.push(axios.request({
method: method.value,
url: url.value,
params: { page: i },
data: cleanFormData.value,
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancelTokens.push(c)
})
}))
if (response.data.length) {
results.value.push(...response.data)
}
} catch (error) {
if (error.name !== 'AbortError') console.log(error)
return
}

await Promise.all(axiosRequests)
.then(response => response.forEach(element => results.value.push(...element.data.data)))
.finally(() => isComplete.value = true)
.catch(error => console.log(error))
const requests = []

for (let i = 2; i <= last_page; i++) {
requests.push(
apiFetch(requestUrl.value, {
method,
params: { page: i },
data: cleanFormData,
signal,
}).then(response => results.value.push(...response.data))
)
}

await Promise.all(requests)
.catch(error => {
if (error.name !== 'AbortError') console.log(error)
})
.finally(() => isComplete.value = true)
}

onBeforeUnmount(() => {
cancelTokens.forEach(cancel => {
if (_.isFunction(cancel)) {
cancel('Load complete resource request canceled.')
}
})
if (abortController) abortController.abort()
})


onBeforeMount(async () => {
await fetchData()
})
Expand Down
7 changes: 4 additions & 3 deletions resources/js/Functions/useResolveId.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import axios from "axios";
import {ref} from "vue";
import { id as resolveId } from '@/routes/resolve'
import { apiFetch } from "@/Functions/apiFetch";

export function useResolveId(id) {

let result = ref()

axios.get(route('resolve.id', id))
.then((response) => result.value = response.data)
apiFetch(resolveId(id).url)
.then((data) => result.value = data)
.catch(error => console.log(error))

return result
Expand Down
Loading
Loading