@devappsnpm/vue-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +82 -0
- package/bin/devapps-vue.js +3 -0
- package/dist/index.cjs +556 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +510 -0
- package/dist/index.js.map +1 -0
- package/dist/ui.cjs +515 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.js +485 -0
- package/dist/ui.js.map +1 -0
- package/package.json +57 -0
- package/stubs/components/component.vue.stub +17 -0
- package/stubs/module/basic/component.vue.stub +9 -0
- package/stubs/module/basic/index.ts.stub +1 -0
- package/stubs/module/crud/modal/components/DeleteModal.vue.stub +35 -0
- package/stubs/module/crud/modal/components/FormModal.vue.stub +88 -0
- package/stubs/module/crud/modal/components/List.vue.stub +60 -0
- package/stubs/module/crud/modal/index.ts.stub +4 -0
- package/stubs/module/crud/modal/router.ts.stub +11 -0
- package/stubs/module/crud/modal/services/service.ts.stub +9 -0
- package/stubs/module/crud/modal/stores/store.ts.stub +9 -0
- package/stubs/module/crud/modal/types/types.ts.stub +16 -0
- package/stubs/module/crud/modal/views/View.vue.stub +83 -0
- package/stubs/module/crud/page/components/DeleteModal.vue.stub +35 -0
- package/stubs/module/crud/page/components/Form.vue.stub +62 -0
- package/stubs/module/crud/page/components/List.vue.stub +42 -0
- package/stubs/module/crud/page/index.ts.stub +4 -0
- package/stubs/module/crud/page/pages/CreatePage.vue.stub +27 -0
- package/stubs/module/crud/page/pages/EditPage.vue.stub +34 -0
- package/stubs/module/crud/page/pages/ListPage.vue.stub +59 -0
- package/stubs/module/crud/page/pages/ShowPage.vue.stub +28 -0
- package/stubs/module/crud/page/router.ts.stub +26 -0
- package/stubs/module/crud/page/services/service.ts.stub +9 -0
- package/stubs/module/crud/page/stores/store.ts.stub +9 -0
- package/stubs/module/crud/page/types/types.ts.stub +16 -0
- package/stubs/module/dashboard/Dashboard.vue.stub +24 -0
- package/stubs/module/dashboard/index.ts.stub +1 -0
- package/stubs/module/dashboard/router.ts.stub +11 -0
- package/stubs/module/resource/List.vue.stub +19 -0
- package/stubs/module/resource/index.ts.stub +3 -0
- package/stubs/module/resource/service.ts.stub +9 -0
- package/stubs/module/resource/store.ts.stub +9 -0
- package/stubs/module/resource/types.ts.stub +10 -0
- package/stubs/pages/page.vue.stub +14 -0
- package/stubs/services/service.ts.stub +13 -0
- package/stubs/stores/store.ts.stub +19 -0
- package/stubs/types/types.ts.stub +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DevApps
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# @devapps/vue-kit
|
|
2
|
+
|
|
3
|
+
Um toolkit Vue 3 + TypeScript que fornece uma base arquitetural robusta para aplicações frontend que consomem APIs Laravel.
|
|
4
|
+
|
|
5
|
+
## 1. Introdução
|
|
6
|
+
O pacote `@devapps/vue-kit` providencia infraestrutura padronizada para:
|
|
7
|
+
- Cliente HTTP (wrapper sobre o Axios)
|
|
8
|
+
- Autenticação (JWT e Sanctum/Cookie)
|
|
9
|
+
- Services Base e Stores do Pinia
|
|
10
|
+
- Formulários com extração de erros 422 do Laravel
|
|
11
|
+
- Gerenciamento de Paginação
|
|
12
|
+
- CLI para scaffolding e geração de módulos (services, stores, types, views)
|
|
13
|
+
- Componentes de UI compartilhados (Modals, Toasts)
|
|
14
|
+
|
|
15
|
+
## 2. Instalação
|
|
16
|
+
```bash
|
|
17
|
+
npm install @devapps/vue-kit
|
|
18
|
+
npm install -D typescript vue pinia
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 3. Configuração
|
|
22
|
+
Configure a instância do `VueKit`, tipicamente no seu `src/main.ts` ou num arquivo dedicado de configuração:
|
|
23
|
+
```ts
|
|
24
|
+
import { createVueKit } from '@devapps/vue-kit'
|
|
25
|
+
|
|
26
|
+
export const vueKit = createVueKit({
|
|
27
|
+
api: {
|
|
28
|
+
baseURL: import.meta.env.VITE_API_URL,
|
|
29
|
+
auth: { driver: 'jwt' } // ou 'cookie'
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 4. Autenticação
|
|
35
|
+
Você pode configurar o driver de autenticação da seguinte maneira:
|
|
36
|
+
- **JWT**: `auth: { driver: 'jwt' }` (Utiliza `localStorage` por padrão para salvar os tokens).
|
|
37
|
+
- **Cookie (Sanctum)**: `auth: { driver: 'cookie', csrf: { enabled: true, endpoint: '/sanctum/csrf-cookie' } }`.
|
|
38
|
+
|
|
39
|
+
## 5. Services & Stores
|
|
40
|
+
Estenda a classe `BaseService` ou utilize `CrudService` para interagir com a sua API.
|
|
41
|
+
|
|
42
|
+
Utilize o `defineCrudStore` para criar rapidamente stores do Pinia completas com todas as funcionalidades de CRUD:
|
|
43
|
+
```ts
|
|
44
|
+
import { defineCrudStore, CrudService } from '@devapps/vue-kit'
|
|
45
|
+
|
|
46
|
+
class CustomerService extends CrudService<Customer> {
|
|
47
|
+
constructor() { super(vueKit.http, '/customers') }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const useCustomerStore = defineCrudStore('customer', () => new CustomerService())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 6. Formulários (Forms)
|
|
54
|
+
A classe `Form<T>` gerencia reativamente o estado, submissão e os erros de validação, integrando-se de maneira transparente aos formatos de resposta padrão do Laravel.
|
|
55
|
+
```ts
|
|
56
|
+
import { Form } from '@devapps/vue-kit'
|
|
57
|
+
|
|
58
|
+
const form = reactive(new Form({ name: '' }))
|
|
59
|
+
await form.submit(async (data) => service.store(data))
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 7. CLI (Geração de Código)
|
|
63
|
+
A CLI gera módulos boilerplate perfeitamente estruturados para acelerar seu desenvolvimento.
|
|
64
|
+
```bash
|
|
65
|
+
npx devapps-vue make:module Customer --type=crud --ui=modal
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Comandos disponíveis:
|
|
69
|
+
- `make:module <Name> [--type=basic|resource|crud|dashboard] [--ui=modal|page]`
|
|
70
|
+
- `make:component <Name>`
|
|
71
|
+
- `make:page <Name>`
|
|
72
|
+
- `make:service <Name>`
|
|
73
|
+
- `make:store <Name>`
|
|
74
|
+
- `make:type <Name>`
|
|
75
|
+
|
|
76
|
+
## 8. Componentes de UI
|
|
77
|
+
Nós fornecemos um entrypoint separado contendo utilitários de UI baseados em Tailwind CSS:
|
|
78
|
+
```ts
|
|
79
|
+
import { BaseModal, ConfirmDeleteModal, ToastContainer, useToast } from '@devapps/vue-kit/ui'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Adicione o `ToastContainer` no layout principal da sua aplicação (App.vue) e utilize o composable `useToast` em qualquer lugar para exibir notificações na tela.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
BaseService: () => BaseService,
|
|
34
|
+
CrudService: () => CrudService,
|
|
35
|
+
Form: () => Form,
|
|
36
|
+
HttpClient: () => HttpClient,
|
|
37
|
+
HttpError: () => HttpError,
|
|
38
|
+
LocalStorageTokenStorage: () => LocalStorageTokenStorage,
|
|
39
|
+
Pagination: () => Pagination,
|
|
40
|
+
SessionStorageTokenStorage: () => SessionStorageTokenStorage,
|
|
41
|
+
createVueKit: () => createVueKit,
|
|
42
|
+
defineCrudStore: () => defineCrudStore
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(src_exports);
|
|
45
|
+
|
|
46
|
+
// src/core/http/HttpClient.ts
|
|
47
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
48
|
+
|
|
49
|
+
// src/core/http/auth/JwtAuthDriver.ts
|
|
50
|
+
var JwtAuthDriver = class {
|
|
51
|
+
constructor(storage) {
|
|
52
|
+
this.storage = storage;
|
|
53
|
+
}
|
|
54
|
+
storage;
|
|
55
|
+
configure(client) {
|
|
56
|
+
client.interceptors.request.use((config) => {
|
|
57
|
+
const token = this.storage.get();
|
|
58
|
+
if (token) {
|
|
59
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
60
|
+
}
|
|
61
|
+
return config;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
clear() {
|
|
65
|
+
this.storage.remove();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/core/http/auth/CookieAuthDriver.ts
|
|
70
|
+
var MUTATING_METHODS = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
|
|
71
|
+
var CookieAuthDriver = class {
|
|
72
|
+
constructor(csrf) {
|
|
73
|
+
this.csrf = csrf;
|
|
74
|
+
}
|
|
75
|
+
csrf;
|
|
76
|
+
csrfInitialization = null;
|
|
77
|
+
configure(client) {
|
|
78
|
+
client.defaults.withCredentials = true;
|
|
79
|
+
client.defaults.withXSRFToken = true;
|
|
80
|
+
client.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
|
|
81
|
+
if (this.csrf?.enabled) {
|
|
82
|
+
client.interceptors.request.use(async (config) => {
|
|
83
|
+
const method = config.method?.toLowerCase();
|
|
84
|
+
if (method && MUTATING_METHODS.has(method)) {
|
|
85
|
+
await this.ensureCsrfCookie(client);
|
|
86
|
+
}
|
|
87
|
+
return config;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
clear() {
|
|
92
|
+
this.csrfInitialization = null;
|
|
93
|
+
}
|
|
94
|
+
ensureCsrfCookie(client) {
|
|
95
|
+
if (!this.csrfInitialization) {
|
|
96
|
+
const endpoint = this.csrf?.endpoint ?? "/sanctum/csrf-cookie";
|
|
97
|
+
this.csrfInitialization = client.get(endpoint).then(() => void 0);
|
|
98
|
+
}
|
|
99
|
+
return this.csrfInitialization;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/core/http/auth/LocalStorageTokenStorage.ts
|
|
104
|
+
var LocalStorageTokenStorage = class {
|
|
105
|
+
constructor(key) {
|
|
106
|
+
this.key = key;
|
|
107
|
+
}
|
|
108
|
+
key;
|
|
109
|
+
get() {
|
|
110
|
+
if (typeof localStorage === "undefined") return null;
|
|
111
|
+
return localStorage.getItem(this.key);
|
|
112
|
+
}
|
|
113
|
+
set(token) {
|
|
114
|
+
if (typeof localStorage === "undefined") return;
|
|
115
|
+
localStorage.setItem(this.key, token);
|
|
116
|
+
}
|
|
117
|
+
remove() {
|
|
118
|
+
if (typeof localStorage === "undefined") return;
|
|
119
|
+
localStorage.removeItem(this.key);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// src/core/http/auth/SessionStorageTokenStorage.ts
|
|
124
|
+
var SessionStorageTokenStorage = class {
|
|
125
|
+
constructor(key) {
|
|
126
|
+
this.key = key;
|
|
127
|
+
}
|
|
128
|
+
key;
|
|
129
|
+
get() {
|
|
130
|
+
if (typeof sessionStorage === "undefined") return null;
|
|
131
|
+
return sessionStorage.getItem(this.key);
|
|
132
|
+
}
|
|
133
|
+
set(token) {
|
|
134
|
+
if (typeof sessionStorage === "undefined") return;
|
|
135
|
+
sessionStorage.setItem(this.key, token);
|
|
136
|
+
}
|
|
137
|
+
remove() {
|
|
138
|
+
if (typeof sessionStorage === "undefined") return;
|
|
139
|
+
sessionStorage.removeItem(this.key);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/core/http/HttpError.ts
|
|
144
|
+
var HttpError = class _HttpError extends Error {
|
|
145
|
+
status;
|
|
146
|
+
errors;
|
|
147
|
+
response;
|
|
148
|
+
constructor(message, status, errors, response) {
|
|
149
|
+
super(message);
|
|
150
|
+
this.name = "HttpError";
|
|
151
|
+
this.status = status;
|
|
152
|
+
this.errors = errors;
|
|
153
|
+
this.response = response;
|
|
154
|
+
}
|
|
155
|
+
static fromAxiosError(error) {
|
|
156
|
+
const status = error.response?.status ?? null;
|
|
157
|
+
const data = error.response?.data;
|
|
158
|
+
const message = (typeof data === "object" && data && "message" in data ? data.message : void 0) ?? error.message ?? "Unexpected HTTP error.";
|
|
159
|
+
const errors = typeof data === "object" && data && "errors" in data ? data.errors : void 0;
|
|
160
|
+
return new _HttpError(message, status, errors, error.response);
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// src/core/http/interceptors/response.interceptor.ts
|
|
165
|
+
function registerResponseInterceptor(client, handlers) {
|
|
166
|
+
client.interceptors.response.use(
|
|
167
|
+
(response) => response,
|
|
168
|
+
(error) => {
|
|
169
|
+
const httpError = HttpError.fromAxiosError(error);
|
|
170
|
+
if (httpError.status === 401) {
|
|
171
|
+
handlers?.onUnauthorized?.();
|
|
172
|
+
}
|
|
173
|
+
return Promise.reject(httpError);
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/core/http/HttpClient.ts
|
|
179
|
+
var HttpClient = class {
|
|
180
|
+
client;
|
|
181
|
+
authDriver;
|
|
182
|
+
constructor(config) {
|
|
183
|
+
this.client = import_axios.default.create({
|
|
184
|
+
baseURL: config.baseURL,
|
|
185
|
+
timeout: config.timeout ?? 3e4,
|
|
186
|
+
headers: {
|
|
187
|
+
Accept: "application/json",
|
|
188
|
+
"Content-Type": "application/json"
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
this.authDriver = this.createAuthDriver(config);
|
|
192
|
+
this.authDriver?.configure(this.client);
|
|
193
|
+
registerResponseInterceptor(this.client, config.handlers);
|
|
194
|
+
}
|
|
195
|
+
createAuthDriver(config) {
|
|
196
|
+
const auth = config.auth;
|
|
197
|
+
if (!auth || auth.driver === "none") {
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
if (auth.driver === "cookie") {
|
|
201
|
+
return new CookieAuthDriver(auth.csrf);
|
|
202
|
+
}
|
|
203
|
+
if (auth.driver === "jwt") {
|
|
204
|
+
return new JwtAuthDriver(this.resolveTokenStorage(auth.tokenStorage));
|
|
205
|
+
}
|
|
206
|
+
return void 0;
|
|
207
|
+
}
|
|
208
|
+
resolveTokenStorage(tokenStorage) {
|
|
209
|
+
if (tokenStorage && "get" in tokenStorage && "set" in tokenStorage) {
|
|
210
|
+
return tokenStorage;
|
|
211
|
+
}
|
|
212
|
+
const key = tokenStorage?.key ?? "auth_token";
|
|
213
|
+
if (tokenStorage?.type === "sessionStorage") {
|
|
214
|
+
return new SessionStorageTokenStorage(key);
|
|
215
|
+
}
|
|
216
|
+
return new LocalStorageTokenStorage(key);
|
|
217
|
+
}
|
|
218
|
+
/** Clears whatever authentication state the configured driver holds (token, csrf, etc). */
|
|
219
|
+
clearAuth() {
|
|
220
|
+
this.authDriver?.clear();
|
|
221
|
+
}
|
|
222
|
+
instance() {
|
|
223
|
+
return this.client;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/config/createVueKit.ts
|
|
228
|
+
function createVueKit(config) {
|
|
229
|
+
const httpConfig = { baseURL: config.api.baseURL };
|
|
230
|
+
if (config.api.timeout !== void 0) httpConfig.timeout = config.api.timeout;
|
|
231
|
+
if (config.api.auth !== void 0) httpConfig.auth = config.api.auth;
|
|
232
|
+
if (config.api.handlers !== void 0) httpConfig.handlers = config.api.handlers;
|
|
233
|
+
const http = new HttpClient(httpConfig);
|
|
234
|
+
return { http, config };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/core/services/BaseService.ts
|
|
238
|
+
var BaseService = class {
|
|
239
|
+
constructor(http) {
|
|
240
|
+
this.http = http;
|
|
241
|
+
}
|
|
242
|
+
http;
|
|
243
|
+
get(url, params) {
|
|
244
|
+
return this.http.instance().get(url, { params });
|
|
245
|
+
}
|
|
246
|
+
post(url, data, params) {
|
|
247
|
+
return this.http.instance().post(url, data, { params });
|
|
248
|
+
}
|
|
249
|
+
put(url, data, params) {
|
|
250
|
+
return this.http.instance().put(url, data, { params });
|
|
251
|
+
}
|
|
252
|
+
patch(url, data, params) {
|
|
253
|
+
return this.http.instance().patch(url, data, { params });
|
|
254
|
+
}
|
|
255
|
+
delete(url, params) {
|
|
256
|
+
return this.http.instance().delete(url, { params });
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
// src/core/services/CrudService.ts
|
|
261
|
+
var CrudService = class extends BaseService {
|
|
262
|
+
constructor(http, resourcePath) {
|
|
263
|
+
super(http);
|
|
264
|
+
this.resourcePath = resourcePath;
|
|
265
|
+
}
|
|
266
|
+
resourcePath;
|
|
267
|
+
/** GET /resource — paginated list */
|
|
268
|
+
async index(params) {
|
|
269
|
+
const response = await this.get(
|
|
270
|
+
this.resourcePath,
|
|
271
|
+
params
|
|
272
|
+
);
|
|
273
|
+
return response.data;
|
|
274
|
+
}
|
|
275
|
+
/** GET /resource/:id */
|
|
276
|
+
async show(id, params) {
|
|
277
|
+
const response = await this.get(
|
|
278
|
+
`${this.resourcePath}/${id}`,
|
|
279
|
+
params
|
|
280
|
+
);
|
|
281
|
+
return response.data;
|
|
282
|
+
}
|
|
283
|
+
/** POST /resource */
|
|
284
|
+
async store(data) {
|
|
285
|
+
const response = await this.post(this.resourcePath, data);
|
|
286
|
+
return response.data;
|
|
287
|
+
}
|
|
288
|
+
/** PUT /resource/:id */
|
|
289
|
+
async update(id, data) {
|
|
290
|
+
const response = await this.put(
|
|
291
|
+
`${this.resourcePath}/${id}`,
|
|
292
|
+
data
|
|
293
|
+
);
|
|
294
|
+
return response.data;
|
|
295
|
+
}
|
|
296
|
+
/** DELETE /resource/:id */
|
|
297
|
+
async destroy(id) {
|
|
298
|
+
await this.delete(`${this.resourcePath}/${id}`);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// src/core/stores/CrudStore.ts
|
|
303
|
+
var import_pinia = require("pinia");
|
|
304
|
+
var import_vue = require("vue");
|
|
305
|
+
|
|
306
|
+
// src/core/pagination/Pagination.ts
|
|
307
|
+
var Pagination = class {
|
|
308
|
+
meta;
|
|
309
|
+
links;
|
|
310
|
+
constructor(meta, links) {
|
|
311
|
+
this.meta = meta;
|
|
312
|
+
this.links = links;
|
|
313
|
+
}
|
|
314
|
+
get currentPage() {
|
|
315
|
+
return this.meta.current_page;
|
|
316
|
+
}
|
|
317
|
+
get lastPage() {
|
|
318
|
+
return this.meta.last_page;
|
|
319
|
+
}
|
|
320
|
+
get perPage() {
|
|
321
|
+
return this.meta.per_page;
|
|
322
|
+
}
|
|
323
|
+
get total() {
|
|
324
|
+
return this.meta.total;
|
|
325
|
+
}
|
|
326
|
+
get from() {
|
|
327
|
+
return this.meta.from;
|
|
328
|
+
}
|
|
329
|
+
get to() {
|
|
330
|
+
return this.meta.to;
|
|
331
|
+
}
|
|
332
|
+
get isFirstPage() {
|
|
333
|
+
return this.meta.current_page === 1;
|
|
334
|
+
}
|
|
335
|
+
get isLastPage() {
|
|
336
|
+
return this.meta.current_page === this.meta.last_page;
|
|
337
|
+
}
|
|
338
|
+
get hasPrevPage() {
|
|
339
|
+
return this.links.prev !== null;
|
|
340
|
+
}
|
|
341
|
+
get hasNextPage() {
|
|
342
|
+
return this.links.next !== null;
|
|
343
|
+
}
|
|
344
|
+
/** Total number of pages. */
|
|
345
|
+
get pageCount() {
|
|
346
|
+
return this.meta.last_page;
|
|
347
|
+
}
|
|
348
|
+
/** Array of page numbers for rendering a paginator UI. */
|
|
349
|
+
pages() {
|
|
350
|
+
return Array.from({ length: this.meta.last_page }, (_, i) => i + 1);
|
|
351
|
+
}
|
|
352
|
+
/** Build the next page params or null if on last page. */
|
|
353
|
+
nextPageParams() {
|
|
354
|
+
if (this.isLastPage) return null;
|
|
355
|
+
return { page: this.meta.current_page + 1 };
|
|
356
|
+
}
|
|
357
|
+
/** Build the prev page params or null if on first page. */
|
|
358
|
+
prevPageParams() {
|
|
359
|
+
if (this.isFirstPage) return null;
|
|
360
|
+
return { page: this.meta.current_page - 1 };
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/core/stores/CrudStore.ts
|
|
365
|
+
function defineCrudStore(id, serviceFactory) {
|
|
366
|
+
return (0, import_pinia.defineStore)(id, () => {
|
|
367
|
+
const service = serviceFactory();
|
|
368
|
+
const items = (0, import_vue.ref)([]);
|
|
369
|
+
const item = (0, import_vue.ref)(null);
|
|
370
|
+
const loading = (0, import_vue.ref)(false);
|
|
371
|
+
const saving = (0, import_vue.ref)(false);
|
|
372
|
+
const deleting = (0, import_vue.ref)(false);
|
|
373
|
+
const error = (0, import_vue.ref)(null);
|
|
374
|
+
const pagination = (0, import_vue.ref)(null);
|
|
375
|
+
function setError(err) {
|
|
376
|
+
if (err instanceof HttpError) {
|
|
377
|
+
error.value = err.message;
|
|
378
|
+
} else if (err instanceof Error) {
|
|
379
|
+
error.value = err.message;
|
|
380
|
+
} else {
|
|
381
|
+
error.value = "An unexpected error occurred.";
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
async function fetchAll(params) {
|
|
385
|
+
loading.value = true;
|
|
386
|
+
error.value = null;
|
|
387
|
+
try {
|
|
388
|
+
const result = await service.index(params);
|
|
389
|
+
items.value = result.data;
|
|
390
|
+
pagination.value = new Pagination(result.meta, result.links);
|
|
391
|
+
} catch (err) {
|
|
392
|
+
setError(err);
|
|
393
|
+
throw err;
|
|
394
|
+
} finally {
|
|
395
|
+
loading.value = false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
async function fetchOne(id2) {
|
|
399
|
+
loading.value = true;
|
|
400
|
+
error.value = null;
|
|
401
|
+
try {
|
|
402
|
+
const result = await service.show(id2);
|
|
403
|
+
item.value = result.data;
|
|
404
|
+
} catch (err) {
|
|
405
|
+
setError(err);
|
|
406
|
+
throw err;
|
|
407
|
+
} finally {
|
|
408
|
+
loading.value = false;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async function create(data) {
|
|
412
|
+
saving.value = true;
|
|
413
|
+
error.value = null;
|
|
414
|
+
try {
|
|
415
|
+
const result = await service.store(data);
|
|
416
|
+
return result.data;
|
|
417
|
+
} catch (err) {
|
|
418
|
+
setError(err);
|
|
419
|
+
throw err;
|
|
420
|
+
} finally {
|
|
421
|
+
saving.value = false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function update(id2, data) {
|
|
425
|
+
saving.value = true;
|
|
426
|
+
error.value = null;
|
|
427
|
+
try {
|
|
428
|
+
const result = await service.update(id2, data);
|
|
429
|
+
return result.data;
|
|
430
|
+
} catch (err) {
|
|
431
|
+
setError(err);
|
|
432
|
+
throw err;
|
|
433
|
+
} finally {
|
|
434
|
+
saving.value = false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function remove(id2) {
|
|
438
|
+
deleting.value = true;
|
|
439
|
+
error.value = null;
|
|
440
|
+
try {
|
|
441
|
+
await service.destroy(id2);
|
|
442
|
+
items.value = items.value.filter((i) => i.uuid !== id2);
|
|
443
|
+
} catch (err) {
|
|
444
|
+
setError(err);
|
|
445
|
+
throw err;
|
|
446
|
+
} finally {
|
|
447
|
+
deleting.value = false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function $reset() {
|
|
451
|
+
items.value = [];
|
|
452
|
+
item.value = null;
|
|
453
|
+
loading.value = false;
|
|
454
|
+
saving.value = false;
|
|
455
|
+
deleting.value = false;
|
|
456
|
+
error.value = null;
|
|
457
|
+
pagination.value = null;
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
items,
|
|
461
|
+
item,
|
|
462
|
+
loading,
|
|
463
|
+
saving,
|
|
464
|
+
deleting,
|
|
465
|
+
error,
|
|
466
|
+
pagination,
|
|
467
|
+
fetchAll,
|
|
468
|
+
fetchOne,
|
|
469
|
+
create,
|
|
470
|
+
update,
|
|
471
|
+
remove,
|
|
472
|
+
$reset
|
|
473
|
+
};
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/core/forms/Form.ts
|
|
478
|
+
var Form = class {
|
|
479
|
+
data;
|
|
480
|
+
errors = {};
|
|
481
|
+
processing = false;
|
|
482
|
+
success = false;
|
|
483
|
+
_initial;
|
|
484
|
+
constructor(initialData) {
|
|
485
|
+
this._initial = { ...initialData };
|
|
486
|
+
this.data = { ...initialData };
|
|
487
|
+
}
|
|
488
|
+
async submit(callback) {
|
|
489
|
+
this.processing = true;
|
|
490
|
+
this.success = false;
|
|
491
|
+
this.clearErrors();
|
|
492
|
+
try {
|
|
493
|
+
await callback(this.data);
|
|
494
|
+
this.success = true;
|
|
495
|
+
} catch (error) {
|
|
496
|
+
if (error instanceof HttpError && error.status === 422 && error.errors) {
|
|
497
|
+
this.setErrors(error.errors);
|
|
498
|
+
} else {
|
|
499
|
+
throw error;
|
|
500
|
+
}
|
|
501
|
+
} finally {
|
|
502
|
+
this.processing = false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
reset() {
|
|
506
|
+
this.data = { ...this._initial };
|
|
507
|
+
this.clearErrors();
|
|
508
|
+
this.success = false;
|
|
509
|
+
this.processing = false;
|
|
510
|
+
}
|
|
511
|
+
resetField(field) {
|
|
512
|
+
this.data[field] = this._initial[field];
|
|
513
|
+
this.clearError(field);
|
|
514
|
+
}
|
|
515
|
+
clearErrors() {
|
|
516
|
+
this.errors = {};
|
|
517
|
+
}
|
|
518
|
+
clearError(field) {
|
|
519
|
+
const updated = { ...this.errors };
|
|
520
|
+
delete updated[field];
|
|
521
|
+
this.errors = updated;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Accepts Laravel-style error bags: `{ field: ['msg1', 'msg2'] }`.
|
|
525
|
+
* Only the first message per field is stored.
|
|
526
|
+
*/
|
|
527
|
+
setErrors(errors) {
|
|
528
|
+
const mapped = {};
|
|
529
|
+
for (const [field, messages] of Object.entries(errors)) {
|
|
530
|
+
const key = field;
|
|
531
|
+
const first = messages[0];
|
|
532
|
+
if (first !== void 0) {
|
|
533
|
+
mapped[key] = first;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
this.errors = mapped;
|
|
537
|
+
}
|
|
538
|
+
/** True when the form has any validation error. */
|
|
539
|
+
get hasErrors() {
|
|
540
|
+
return Object.keys(this.errors).length > 0;
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
544
|
+
0 && (module.exports = {
|
|
545
|
+
BaseService,
|
|
546
|
+
CrudService,
|
|
547
|
+
Form,
|
|
548
|
+
HttpClient,
|
|
549
|
+
HttpError,
|
|
550
|
+
LocalStorageTokenStorage,
|
|
551
|
+
Pagination,
|
|
552
|
+
SessionStorageTokenStorage,
|
|
553
|
+
createVueKit,
|
|
554
|
+
defineCrudStore
|
|
555
|
+
});
|
|
556
|
+
//# sourceMappingURL=index.cjs.map
|