first commit
This commit is contained in:
commit
eb287dccc1
6 changed files with 6208 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
dist
|
5955
package-lock.json
generated
Normal file
5955
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
32
package.json
Normal file
32
package.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"name": "create-json-from-collection",
|
||||||
|
"description": "Please enter a description for your extension",
|
||||||
|
"icon": "extension",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"keywords": [
|
||||||
|
"directus",
|
||||||
|
"directus-extension",
|
||||||
|
"directus-extension-hook"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"directus:extension": {
|
||||||
|
"type": "hook",
|
||||||
|
"path": "dist/index.js",
|
||||||
|
"source": "src/index.ts",
|
||||||
|
"host": "^10.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "directus-extension build",
|
||||||
|
"dev": "directus-extension build -w --no-minify",
|
||||||
|
"link": "directus-extension link",
|
||||||
|
"validate": "directus-extension validate"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@directus/extensions-sdk": "16.0.0",
|
||||||
|
"@types/node": "^24.3.0",
|
||||||
|
"typescript": "^5.9.2"
|
||||||
|
}
|
||||||
|
}
|
179
src/index.ts
Normal file
179
src/index.ts
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
import { defineHook } from "@directus/extensions-sdk"
|
||||||
|
import { writeFile } from "fs/promises"
|
||||||
|
import { join } from "path"
|
||||||
|
import { formatDate } from "./utils"
|
||||||
|
|
||||||
|
export default defineHook(({ action }, { env }) => {
|
||||||
|
const mapsGraphQLQuery = `
|
||||||
|
query getMaps {
|
||||||
|
maps {
|
||||||
|
id
|
||||||
|
property_name
|
||||||
|
description
|
||||||
|
date_created
|
||||||
|
photos {
|
||||||
|
photo {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
property_code_type
|
||||||
|
property_type
|
||||||
|
geoFeatures
|
||||||
|
property_rooms
|
||||||
|
property_surface
|
||||||
|
property_sell
|
||||||
|
property_fees
|
||||||
|
property_price
|
||||||
|
property_dpe_conso
|
||||||
|
property_dpe_ges
|
||||||
|
property_dpe_date
|
||||||
|
property_depenses_min
|
||||||
|
property_depenses_max
|
||||||
|
property_depenses_est
|
||||||
|
property_dpe_date_price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const handleMapsEvent = async () => {
|
||||||
|
const response = await fetch(`${env.PUBLIC_URL}/graphql`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
Authorization: `Bearer ${env.API_TOKEN}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ query: mapsGraphQLQuery }),
|
||||||
|
})
|
||||||
|
|
||||||
|
const json = await response.json()
|
||||||
|
const allMaps = json.data?.maps ?? []
|
||||||
|
|
||||||
|
const transformMapData = (maps: any[]): {} => {
|
||||||
|
const annonces: any[] = maps.map((map) => ({
|
||||||
|
annonce: {
|
||||||
|
// ANNONCE
|
||||||
|
reference: map.id,
|
||||||
|
titre: map.property_name,
|
||||||
|
texte: map?.description ?? "Pas encore de description.",
|
||||||
|
date_saisie: formatDate(map.date_created),
|
||||||
|
photos:
|
||||||
|
map?.photos?.map((img: any) => ({
|
||||||
|
photo: `${env.PUBLIC_URL}/assets/${img.photo.id}`,
|
||||||
|
})) ?? null,
|
||||||
|
|
||||||
|
// BIEN
|
||||||
|
code_type: map.property_code_type ?? undefined,
|
||||||
|
libelle_type: map.property_type ?? undefined,
|
||||||
|
code_postal: map.geoFeatures.properties.postcode,
|
||||||
|
ville: map.geoFeatures.properties.city,
|
||||||
|
nb_pieces_logement: map.property_rooms,
|
||||||
|
surface: map.property_surface ?? undefined,
|
||||||
|
adresse: map.geoFeatures.properties.name ?? undefined,
|
||||||
|
|
||||||
|
// PRESTATION
|
||||||
|
type: map.property_sell,
|
||||||
|
frais_agence: map.property_fees,
|
||||||
|
// si vente
|
||||||
|
prix: map.property_sell !== "L" ? map.property_price : undefined,
|
||||||
|
// si location
|
||||||
|
loyer_mensuel:
|
||||||
|
map.property_sell === "L" ? map.property_price : undefined,
|
||||||
|
|
||||||
|
// DPE
|
||||||
|
dpe_valeur_conso: map.property_dpe_conso,
|
||||||
|
dpe_valeur_ges: map.property_dpe_ges,
|
||||||
|
dpe_date_realisation: formatDate(map.property_dpe_date),
|
||||||
|
montant_depenses_energies_min: map.property_depenses_min,
|
||||||
|
montant_depenses_energies_max: map.property_depenses_max,
|
||||||
|
montant_depenses_energies_estime: map.property_depenses_est,
|
||||||
|
date_indice_prix_energies: map.property_dpe_date_price,
|
||||||
|
|
||||||
|
// Array handling with optional chaining
|
||||||
|
categories:
|
||||||
|
map?.categories?.map((cat: any) => cat?.name) ?? undefined,
|
||||||
|
|
||||||
|
// Date handling
|
||||||
|
updatedAt: map?.date_updated,
|
||||||
|
|
||||||
|
// Complex nested objects
|
||||||
|
settings: {
|
||||||
|
zoom: map?.settings?.zoom ?? 10,
|
||||||
|
theme: map?.settings?.theme ?? "default",
|
||||||
|
markers: map?.settings?.markers?.enabled ?? false,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Conditional assignment using optional chaining
|
||||||
|
...(map?.location && {
|
||||||
|
latitude: map.location?.lat,
|
||||||
|
longitude: map.location?.lng,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Keep some original structure if it exists
|
||||||
|
...(map?.metadata && { originalMetadata: map.metadata }),
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
return { client: [...annonces] }
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply key remapping
|
||||||
|
const transformedMaps = Array.isArray(allMaps)
|
||||||
|
? transformMapData(allMaps)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const jsonData = JSON.stringify(transformedMaps, null, 2)
|
||||||
|
|
||||||
|
// write to existing file
|
||||||
|
const filePath = join(env.OUTPUT_DIR, env.OUTPUT_FILENAME)
|
||||||
|
await writeFile(filePath, jsonData, "utf8")
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Updates the maps JSON file by fetching all maps and writing to file
|
||||||
|
// const updateMapsFile = async (): Promise<void> => {
|
||||||
|
// try {
|
||||||
|
// const schema = await getSchema()
|
||||||
|
// const itemsService = new ItemsService(COLLECTION_NAME, {
|
||||||
|
// schema,
|
||||||
|
// accountability: null, // Use null for full access in hooks
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // get all items in collection
|
||||||
|
// const allMaps = await itemsService.readByQuery({})
|
||||||
|
|
||||||
|
// console.log(allMaps)
|
||||||
|
|
||||||
|
// // Transform the data before serializing
|
||||||
|
// const transformedMaps = Array.isArray(allMaps)
|
||||||
|
// ? transformMapData(allMaps)
|
||||||
|
// : []
|
||||||
|
|
||||||
|
// // format items to JSON
|
||||||
|
// const jsonData = JSON.stringify(transformedMaps, null, 2)
|
||||||
|
// console.log(
|
||||||
|
// `Updating maps file with ${
|
||||||
|
// Array.isArray(allMaps) ? allMaps.length : 0
|
||||||
|
// } items`
|
||||||
|
// )
|
||||||
|
|
||||||
|
// const filePath = join(OUTPUT_DIR, OUTPUT_FILENAME)
|
||||||
|
// // write JSON to file (already exists)
|
||||||
|
// await writeFile(filePath, jsonData, "utf8")
|
||||||
|
|
||||||
|
// console.log(`Maps file successfully updated at: ${filePath}`)
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Error updating maps file:", error)
|
||||||
|
// throw error
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Register hook for all CRUD operations on maps
|
||||||
|
const mapsActions = [
|
||||||
|
`${env.COLLECTION}.items.create`,
|
||||||
|
`${env.COLLECTION}.items.update`,
|
||||||
|
`${env.COLLECTION}.items.delete`,
|
||||||
|
]
|
||||||
|
|
||||||
|
mapsActions.forEach((actionName) => {
|
||||||
|
action(actionName, handleMapsEvent)
|
||||||
|
})
|
||||||
|
})
|
11
src/utils.ts
Normal file
11
src/utils.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export const formatDate = (
|
||||||
|
date: string,
|
||||||
|
options?: Intl.DateTimeFormatOptions
|
||||||
|
) => {
|
||||||
|
const defaultOptions: Intl.DateTimeFormatOptions = {
|
||||||
|
year: "numeric",
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
}
|
||||||
|
return new Date(date).toLocaleDateString("fr", options ?? defaultOptions)
|
||||||
|
}
|
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2019",
|
||||||
|
"lib": ["ES2019", "DOM"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"strictBindCallApply": true,
|
||||||
|
"strictPropertyInitialization": true,
|
||||||
|
"resolveJsonModule": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"rootDir": "./src"
|
||||||
|
},
|
||||||
|
"include": ["./src/**/*.ts"]
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue