quickbooks-medusa-plugin 0.0.3 → 0.0.4

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/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "quickbooks-medusa-plugin",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Medusa v2 plugin for QuickBooks Online bi-directional sync (Customers, Products, Orders, Inventory).",
5
5
  "author": "bootssecurity",
6
6
  "license": "MIT",
7
7
  "files": [
8
- ".medusa/server",
9
- "src/admin"
8
+ ".medusa/server"
10
9
  ],
11
10
  "exports": {
12
11
  "./package.json": "./package.json",
@@ -16,9 +15,9 @@
16
15
  "./providers/*": "./.medusa/server/src/providers/*/index.js",
17
16
  "./*": "./.medusa/server/src/*.js",
18
17
  "./admin": {
19
- "import": "./src/admin/index.ts",
20
- "require": "./src/admin/index.ts",
21
- "default": "./src/admin/index.ts"
18
+ "import": "./.medusa/server/src/admin/index.mjs",
19
+ "require": "./.medusa/server/src/admin/index.js",
20
+ "default": "./.medusa/server/src/admin/index.js"
22
21
  }
23
22
  },
24
23
  "keywords": [
@@ -31,7 +30,7 @@
31
30
  "intuit"
32
31
  ],
33
32
  "scripts": {
34
- "build": "medusa plugin:build",
33
+ "build": "medusa plugin:build && mkdir -p .medusa/server/src/admin && cp -r src/admin/routes .medusa/server/src/admin/",
35
34
  "dev": "medusa plugin:develop",
36
35
  "prepublishOnly": "medusa plugin:build"
37
36
  },
@@ -1,31 +0,0 @@
1
- # Admin Customizations
2
-
3
- You can extend the Medusa Admin to add widgets and new pages. Your customizations interact with API routes to provide merchants with custom functionalities.
4
-
5
- ## Example: Create a Widget
6
-
7
- A widget is a React component that can be injected into an existing page in the admin dashboard.
8
-
9
- For example, create the file `src/admin/widgets/product-widget.tsx` with the following content:
10
-
11
- ```tsx title="src/admin/widgets/product-widget.tsx"
12
- import { defineWidgetConfig } from "@medusajs/admin-sdk"
13
-
14
- // The widget
15
- const ProductWidget = () => {
16
- return (
17
- <div>
18
- <h2>Product Widget</h2>
19
- </div>
20
- )
21
- }
22
-
23
- // The widget's configurations
24
- export const config = defineWidgetConfig({
25
- zone: "product.details.after",
26
- })
27
-
28
- export default ProductWidget
29
- ```
30
-
31
- This inserts a widget with the text “Product Widget” at the end of a product’s details page.
@@ -1,58 +0,0 @@
1
- # Admin Customizations Translations
2
-
3
- The Medusa Admin dashboard supports multiple languages for its interface. Medusa uses [react-i18next](https://react.i18next.com/) to manage translations in the admin dashboard.
4
-
5
- To add translations, create JSON translation files for each language under the `src/admin/i18n/json` directory. For example, create the `src/admin/i18n/json/en.json` file with the following content:
6
-
7
- ```json
8
- {
9
- "brands": {
10
- "title": "Brands",
11
- "description": "Manage your product brands"
12
- },
13
- "done": "Done"
14
- }
15
- ```
16
-
17
- Then, export the translations in `src/admin/i18n/index.ts`:
18
-
19
- ```ts
20
- import en from "./json/en.json" with { type: "json" }
21
-
22
- export default {
23
- en: {
24
- translation: en,
25
- },
26
- }
27
- ```
28
-
29
- Finally, use translations in your admin widgets and routes using the `useTranslation` hook:
30
-
31
- ```tsx
32
- import { defineWidgetConfig } from "@medusajs/admin-sdk"
33
- import { Button, Container, Heading } from "@medusajs/ui"
34
- import { useTranslation } from "react-i18next"
35
-
36
- const ProductWidget = () => {
37
- const { t } = useTranslation()
38
- return (
39
- <Container className="p-0">
40
- <div className="flex items-center justify-between px-6 py-4">
41
- <Heading level="h2">{t("brands.title")}</Heading>
42
- <p>{t("brands.description")}</p>
43
- </div>
44
- <div className="flex justify-end px-6 py-4">
45
- <Button variant="primary">{t("done")}</Button>
46
- </div>
47
- </Container>
48
- )
49
- }
50
-
51
- export const config = defineWidgetConfig({
52
- zone: "product.details.before",
53
- })
54
-
55
- export default ProductWidget
56
- ```
57
-
58
- Learn more about translating admin extensions in the [Translate Admin Customizations](https://docs.medusajs.com/learn/fundamentals/admin/translations) documentation.
@@ -1 +0,0 @@
1
- export default {}
@@ -1,2 +0,0 @@
1
- // Entry point for admin extensions
2
- export default {};
@@ -1,284 +0,0 @@
1
- import { defineRouteConfig } from "@medusajs/admin-sdk"
2
- import { Container, Heading, Text, Input, Button, Label, Switch, toast, Badge } from "@medusajs/ui"
3
- import { useEffect, useState } from "react"
4
- import { useForm, Controller } from "react-hook-form"
5
-
6
- // Types
7
- type QuickBooksSettings = {
8
- id: string
9
- income_account_id?: string
10
- cogs_account_id?: string
11
- asset_account_id?: string
12
- auto_sync_customers: boolean
13
- auto_sync_products: boolean
14
- auto_sync_orders: boolean
15
- reverse_sync_customers: boolean
16
- reverse_sync_products: boolean
17
- reverse_sync_orders: boolean
18
- }
19
-
20
- type ConnectionStatus = {
21
- is_connected: boolean
22
- company_name?: string
23
- realm_id?: string
24
- }
25
-
26
- const QuickBooksSettingsPage = () => {
27
- const [settings, setSettings] = useState<QuickBooksSettings | null>(null)
28
- const [connection, setConnection] = useState<ConnectionStatus | null>(null)
29
- const [isLoading, setIsLoading] = useState(true)
30
- const [isSaving, setIsSaving] = useState(false)
31
-
32
- const { control, handleSubmit, reset } = useForm<QuickBooksSettings>({
33
- defaultValues: {
34
- auto_sync_customers: false,
35
- auto_sync_products: false,
36
- auto_sync_orders: false,
37
- reverse_sync_customers: false,
38
- reverse_sync_products: false,
39
- reverse_sync_orders: false,
40
- }
41
- })
42
-
43
- useEffect(() => {
44
- fetchData()
45
- }, [])
46
-
47
- const fetchData = async () => {
48
- try {
49
- setIsLoading(true)
50
- // Fetch connection status
51
- const connRes = await fetch("/admin/quickbooks/connection")
52
- const connData = await connRes.json()
53
- setConnection(connData)
54
-
55
- // Fetch settings
56
- const setRes = await fetch("/admin/quickbooks/settings")
57
- const setData = await setRes.json()
58
-
59
- if (setData.settings) {
60
- setSettings(setData.settings)
61
- reset(setData.settings)
62
- }
63
- } catch (err) {
64
- toast.error("Failed to load QuickBooks data")
65
- } finally {
66
- setIsLoading(false)
67
- }
68
- }
69
-
70
- const onSubmit = async (data: QuickBooksSettings) => {
71
- try {
72
- setIsSaving(true)
73
- const res = await fetch("/admin/quickbooks/settings", {
74
- method: "PUT",
75
- headers: { "Content-Type": "application/json" },
76
- body: JSON.stringify(data)
77
- })
78
-
79
- if (res.ok) {
80
- toast.success("Settings saved successfully")
81
- const updated = await res.json()
82
- setSettings(updated.settings)
83
- reset(updated.settings)
84
- } else {
85
- throw new Error("Failed to save")
86
- }
87
- } catch (err) {
88
- toast.error("Failed to save settings")
89
- } finally {
90
- setIsSaving(false)
91
- }
92
- }
93
-
94
- const handleConnect = async () => {
95
- try {
96
- const res = await fetch("/admin/quickbooks/connect")
97
- const data = await res.json()
98
- if (data.url) {
99
- window.location.href = data.url
100
- } else {
101
- toast.error("Failed to get authorization URL")
102
- }
103
- } catch (err) {
104
- toast.error("Connection error")
105
- }
106
- }
107
-
108
- const handleDisconnect = async () => {
109
- if (!confirm("Are you sure you want to disconnect?")) return
110
-
111
- try {
112
- const res = await fetch("/admin/quickbooks/disconnect", {
113
- method: "POST"
114
- })
115
- if (res.ok) {
116
- toast.success("Disconnected from QuickBooks")
117
- fetchData()
118
- } else {
119
- toast.error("Failed to disconnect")
120
- }
121
- } catch (err) {
122
- toast.error("Disconnection error")
123
- }
124
- }
125
-
126
- if (isLoading) return <Text>Loading...</Text>
127
-
128
- return (
129
- <Container className="p-8 flex flex-col gap-y-8">
130
- <div>
131
- <Heading level="h1">QuickBooks Integration</Heading>
132
- <Text className="text-ui-fg-subtle">
133
- Manage your connection and synchronization settings.
134
- </Text>
135
- </div>
136
-
137
- {/* Connection Status */}
138
- <div className="flex flex-col gap-y-4 p-4 border rounded-lg bg-ui-bg-base">
139
- <div className="flex items-center justify-between">
140
- <div className="flex flex-col gap-y-1">
141
- <Heading level="h2">Connection Status</Heading>
142
- <div className="flex items-center gap-x-2">
143
- <Text>Status:</Text>
144
- {connection?.is_connected ? (
145
- <Badge color="green">Connected</Badge>
146
- ) : (
147
- <Badge color="red">Disconnected</Badge>
148
- )}
149
- </div>
150
- {connection?.is_connected && (
151
- <Text className="text-ui-fg-subtle text-small">
152
- Connected to: {connection.company_name} (Realm ID: {connection.realm_id})
153
- </Text>
154
- )}
155
- </div>
156
- <div>
157
- {connection?.is_connected ? (
158
- <Button variant="danger" onClick={handleDisconnect}>Disconnect</Button>
159
- ) : (
160
- <Button onClick={handleConnect}>Connect to QuickBooks</Button>
161
- )}
162
- </div>
163
- </div>
164
- </div>
165
-
166
- {/* Settings Form */}
167
- <form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-y-6">
168
-
169
- <div className="flex flex-col gap-y-4">
170
- <Heading level="h2">Account Mappings</Heading>
171
- <Text className="text-ui-fg-subtle">Enter the QuickBooks Account IDs (Numeric) for transactions.</Text>
172
-
173
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
174
- <div className="flex flex-col gap-y-2">
175
- <Label>Income Account ID</Label>
176
- <Controller
177
- control={control}
178
- name="income_account_id"
179
- render={({ field }) => <Input {...field} placeholder="e.g. 1" />}
180
- />
181
- </div>
182
- <div className="flex flex-col gap-y-2">
183
- <Label>COGS Account ID</Label>
184
- <Controller
185
- control={control}
186
- name="cogs_account_id"
187
- render={({ field }) => <Input {...field} placeholder="e.g. 50" />}
188
- />
189
- </div>
190
- <div className="flex flex-col gap-y-2">
191
- <Label>Asset Account ID</Label>
192
- <Controller
193
- control={control}
194
- name="asset_account_id"
195
- render={({ field }) => <Input {...field} placeholder="e.g. 80" />}
196
- />
197
- </div>
198
- </div>
199
- </div>
200
-
201
- <div className="flex flex-col gap-y-4">
202
- <Heading level="h2">Synchronization Settings</Heading>
203
-
204
- <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
205
- {/* Auto Sync (Medusa -> QB) */}
206
- <div className="flex flex-col gap-y-4">
207
- <Text weight="plus">Auto-Sync (Medusa to QuickBooks)</Text>
208
-
209
- <div className="flex items-center justify-between">
210
- <Label>Sync Customers</Label>
211
- <Controller
212
- control={control}
213
- name="auto_sync_customers"
214
- render={({ field: { value, onChange } }) => (
215
- <Switch checked={value} onCheckedChange={onChange} />
216
- )}
217
- />
218
- </div>
219
- <div className="flex items-center justify-between">
220
- <Label>Sync Products</Label>
221
- <Controller
222
- control={control}
223
- name="auto_sync_products"
224
- render={({ field: { value, onChange } }) => (
225
- <Switch checked={value} onCheckedChange={onChange} />
226
- )}
227
- />
228
- </div>
229
- <div className="flex items-center justify-between">
230
- <Label>Sync Orders</Label>
231
- <Controller
232
- control={control}
233
- name="auto_sync_orders"
234
- render={({ field: { value, onChange } }) => (
235
- <Switch checked={value} onCheckedChange={onChange} />
236
- )}
237
- />
238
- </div>
239
- </div>
240
-
241
- {/* Reverse Sync (QB -> Medusa) */}
242
- <div className="flex flex-col gap-y-4">
243
- <Text weight="plus">Reverse Sync (QuickBooks to Medusa)</Text>
244
- <Text className="text-ui-fg-subtle text-small">Requires Webhooks to be configured in QuickBooks.</Text>
245
-
246
- <div className="flex items-center justify-between">
247
- <Label>Sync Customers (Recieve Updates)</Label>
248
- <Controller
249
- control={control}
250
- name="reverse_sync_customers"
251
- render={({ field: { value, onChange } }) => (
252
- <Switch checked={value} onCheckedChange={onChange} />
253
- )}
254
- />
255
- </div>
256
- <div className="flex items-center justify-between">
257
- <Label>Sync Products (Receive Updates)</Label>
258
- <Controller
259
- control={control}
260
- name="reverse_sync_products"
261
- render={({ field: { value, onChange } }) => (
262
- <Switch checked={value} onCheckedChange={onChange} />
263
- )}
264
- />
265
- </div>
266
- {/* Orders rarely reverse synced for status only, maybe later */}
267
- </div>
268
- </div>
269
- </div>
270
-
271
- <div className="flex justify-end">
272
- <Button type="submit" isLoading={isSaving}>Save Settings</Button>
273
- </div>
274
- </form>
275
- </Container>
276
- )
277
- }
278
-
279
- export const config = defineRouteConfig({
280
- label: "QuickBooks",
281
- icon: "currency-dollar",
282
- })
283
-
284
- export default QuickBooksSettingsPage
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "useDefineForClassFields": true,
5
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
- "module": "ESNext",
7
- "skipLibCheck": true,
8
-
9
- /* Bundler mode */
10
- "moduleResolution": "bundler",
11
- "allowImportingTsExtensions": true,
12
- "resolveJsonModule": true,
13
- "isolatedModules": true,
14
- "noEmit": true,
15
- "jsx": "react-jsx",
16
-
17
- /* Linting */
18
- "strict": true,
19
- "noUnusedLocals": true,
20
- "noUnusedParameters": true,
21
- "noFallthroughCasesInSwitch": true
22
- },
23
- "include": ["."]
24
- }
@@ -1 +0,0 @@
1
- /// <reference types="vite/client" />