# SOP – CadRoute MVP

## Obiettivo
Realizzare un'app desktop (.exe) che:
- accetti una cartella o uno ZIP
- legga file CAD (.prt .asm .dwr)
- analizzi nome file ed estensione
- applichi regole di routing
- copi i file verso destinazioni configurate (anche share di rete)

Tecnologie:
- Node.js
- Electron
- unzipper
- fs-extra

---

## 1. Prerequisiti

Installare:
- Node.js LTS
- Git
- Visual Studio Code / VSCodium

Verifica:

```
node -v
npm -v
```

---

## 2. Creazione progetto

```
mkdir cadroute
cd cadroute
npm init -y
```

Installare dipendenze:

```
npm install electron unzipper fs-extra
npm install electron-builder --save-dev
```

---

## 3. Struttura progetto

```
cadroute
│
├── package.json
├── main.js
├── preload.js
│
├── renderer
│   ├── index.html
│   └── renderer.js
│
├── services
│   ├── router.js
│   ├── zipProcessor.js
│   └── folderProcessor.js
```

---

## 4. Configurazione runtime

La destinazione viene gestita in memoria in `main.js` e modificata dalla UI durante l'esecuzione.

---

## 5. Motore di routing

`services/router.js`

```
const path = require("path")

function findDestination(filename, config){

    const ext = path.extname(filename).slice(1)

    for(const rule of config.rules){

        if(rule.ext !== ext)
            continue

        if(!rule.pattern)
            return rule.destination

        const regex = new RegExp(rule.pattern)

        if(regex.test(filename))
            return rule.destination
    }

    return null
}

module.exports = { findDestination }
```

---

## 7. Processare una cartella

`services/folderProcessor.js`

```
const fs = require("fs-extra")
const path = require("path")
const { findDestination } = require("./router")

async function processFolder(folder, config){

    const files = await fs.readdir(folder)

    for(const file of files){

        const src = path.join(folder,file)

        const destDir = findDestination(file, config)

        if(!destDir) continue

        const dest = path.join(destDir,file)

        await fs.copy(src,dest)
    }
}

module.exports = { processFolder }
```

---

## 8. Processare ZIP

`services/zipProcessor.js`

```
const unzipper = require("unzipper")
const fs = require("fs-extra")
const path = require("path")
const { findDestination } = require("./router")

async function processZip(zipPath, config){

    const stream = fs.createReadStream(zipPath)
        .pipe(unzipper.Parse())

    for await (const entry of stream){

        const file = entry.path

        const destDir = findDestination(file, config)

        if(!destDir){
            entry.autodrain()
            continue
        }

        const dest = path.join(destDir,path.basename(file))

        entry.pipe(fs.createWriteStream(dest))
    }
}

module.exports = { processZip }
```

---

## 9. Main Electron

`main.js`

```
const { app, BrowserWindow, dialog, ipcMain } = require("electron")
const path = require("path")

const { processFolder } = require("./services/folderProcessor")
const { processZip } = require("./services/zipProcessor")
const CAD_EXTENSIONS = ["prt", "asm", "dwr"]
const DEFAULT_DESTINATION = "./output/cad"

let config = {
    destination: DEFAULT_DESTINATION,
    rules: CAD_EXTENSIONS.map((ext) => ({ ext, destination: DEFAULT_DESTINATION }))
}

function createWindow(){

    const win = new BrowserWindow({
        width:800,
        height:600,
        webPreferences:{
            preload: path.join(__dirname,"preload.js")
        }
    })

    win.loadFile("renderer/index.html")
}

app.whenReady().then(createWindow)

ipcMain.handle("select-folder", async () => {

    const result = await dialog.showOpenDialog({
        properties:["openDirectory"]
    })

    if(result.canceled) return

    await processFolder(result.filePaths[0], config)

})

ipcMain.handle("select-zip", async () => {

    const result = await dialog.showOpenDialog({
        filters:[{ name:"Zip", extensions:["zip"] }]
    })

    if(result.canceled) return

    await processZip(result.filePaths[0], config)

})
```

---

## 10. Preload

`preload.js`

```
const { contextBridge, ipcRenderer } = require("electron")

contextBridge.exposeInMainWorld("api", {

    selectFolder: () => ipcRenderer.invoke("select-folder"),

    selectZip: () => ipcRenderer.invoke("select-zip")

})
```

---

## 11. UI minima

`renderer/index.html`

```
<h2>CadRoute</h2>

<button onclick="window.api.selectFolder()">
Process Folder
</button>

<button onclick="window.api.selectZip()">
Process ZIP
</button>
```

---

## 12. Avvio applicazione

Nel package.json aggiungere:

```
"scripts": {
  "start": "electron ."
}
```

Avviare:

```
npm start
```

---

## 13. Build .exe

Aggiungere nel package.json:

```
"build": {
  "appId": "com.cadroute",
  "win": {
    "target": "nsis"
  }
}
```

Build:

```
npx electron-builder
```

Output:

```
dist/
   CadRoute Setup.exe
```

---

## MVP completato quando

- l'app si avvia
- si seleziona ZIP o cartella
- i file .prt .asm .dwr vengono analizzati
- i file vengono copiati nelle destinazioni configurate

---

## Migliorie future

- progress bar
- drag and drop ZIP
- modifica regole avanzate da UI
- logging operazioni
- parallelismo file
- watch folder automatico
