diff --git a/docusaurus/docs/getting-started.md b/docusaurus/docs/getting-started.md index ef7da3006f0..c32ec970f3d 100644 --- a/docusaurus/docs/getting-started.md +++ b/docusaurus/docs/getting-started.md @@ -1,162 +1,374 @@ ---- -id: getting-started -title: Getting Started ---- - -Create React App is an officially supported way to create single-page React -applications. It offers a modern build setup with no configuration. - -## Quick Start - -```sh -npx create-react-app my-app -cd my-app -npm start -``` - -> If you've previously installed `create-react-app` globally via `npm install -g create-react-app`, we recommend you uninstall the package using `npm uninstall -g create-react-app` or `yarn global remove create-react-app` to ensure that `npx` always uses the latest version. - -_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) comes with npm 5.2+ and higher, see [instructions for older npm versions](https://gist.github.com/gaearon/4064d3c23a77c74a3614c498a8bb1c5f))_ - -Then open [http://localhost:3000/](http://localhost:3000/) to see your app. - -When you’re ready to deploy to production, create a minified bundle with `npm run build`. - -

-npm start -

- -### Get Started Immediately - -You **don’t** need to install or configure tools like webpack or Babel. They are preconfigured and hidden so that you can focus on the code. - -Create a project, and you’re good to go. - -## Creating an App - -**You’ll need to have Node >= 14 on your local development machine** (but it’s not required on the server). You can use [nvm](https://github.com/creationix/nvm#installation) (macOS/Linux) or [nvm-windows](https://github.com/coreybutler/nvm-windows#node-version-manager-nvm-for-windows) to switch Node versions between different projects. - -To create a new app, you may choose one of the following methods: - -### npx - -```sh -npx create-react-app@latest my-app -``` - -_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) comes with npm 5.2+ and higher, see [instructions for older npm versions](https://gist.github.com/gaearon/4064d3c23a77c74a3614c498a8bb1c5f))_ - -### npm - -```sh -npm init react-app my-app -``` - -_`npm init ` is available in npm 6+_ - -### Yarn - -```sh -yarn create react-app my-app -``` - -_`yarn create` is available in Yarn 0.25+_ - -### Selecting a template - -You can now optionally start a new app from a template by appending `--template [template-name]` to the creation command. - -If you don't select a template, we'll create your project with our base template. - -Templates are always named in the format `cra-template-[template-name]`, however you only need to provide the `[template-name]` to the creation command. - -```sh -npx create-react-app my-app --template [template-name] -``` - -> You can find a list of available templates by searching for ["cra-template-\*"](https://www.npmjs.com/search?q=cra-template-*) on npm. - -Our [Custom Templates](custom-templates.md) documentation describes how you can build your own template. - -#### Creating a TypeScript app - -You can start a new TypeScript app using templates. To use our provided TypeScript template, append `--template typescript` to the creation command. - -```sh -npx create-react-app my-app --template typescript -``` - -If you already have a project and would like to add TypeScript, see our [Adding TypeScript](adding-typescript.md) documentation. - -### Selecting a package manager - -When you create a new app, the CLI will use [npm](https://docs.npmjs.com) or [Yarn](https://yarnpkg.com/) to install dependencies, depending on which tool you use to run `create-react-app`. For example: - -```sh -# Run this to use npm -npx create-react-app my-app -# Or run this to use yarn -yarn create react-app my-app -``` - -## Output - -Running any of these commands will create a directory called `my-app` inside the current folder. Inside that directory, it will generate the initial project structure and install the transitive dependencies: - -``` -my-app -├── README.md -├── node_modules -├── package.json -├── .gitignore -├── public -│ ├── favicon.ico -│ ├── index.html -│ ├── logo192.png -│ ├── logo512.png -│ ├── manifest.json -│ └── robots.txt -└── src - ├── App.css - ├── App.js - ├── App.test.js - ├── index.css - ├── index.js - ├── logo.svg - ├── serviceWorker.js - └── setupTests.js -``` - -No configuration or complicated folder structures, only the files you need to build your app. Once the installation is done, you can open your project folder: - -```sh -cd my-app -``` - -## Scripts - -Inside the newly created project, you can run some built-in commands: - -### `npm start` or `yarn start` - -Runs the app in development mode. Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will automatically reload if you make changes to the code. You will see the build errors and lint warnings in the console. - -

-Build errors -

- -### `npm test` or `yarn test` - -Runs the test watcher in an interactive mode. By default, runs tests related to files changed since the last commit. - -[Read more about testing](running-tests.md). - -### `npm run build` or `yarn build` - -Builds the app for production to the `build` folder. It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes. - -Your app is ready to be deployed. +import React, { useEffect, useState } from "react"; + +// Compatibility App - Single-file React component +// Usage: +// - Paste this component into a Create React App / Vite project. +// - Tailwind CSS classes are used for styling; if Tailwind is not available the UI will still work but look plain. +// - The app stores data in localStorage under keys: 'parts', 'rules'. + +export default function CompatibilityApp() { + // Parts: { id, name, type, attrs: { key: value } } + const [parts, setParts] = useState(() => { + try { + return JSON.parse(localStorage.getItem("parts") || "[]"); + } catch (e) { + return []; + } + }); + const [rules, setRules] = useState(() => { + try { + return JSON.parse(localStorage.getItem("rules") || "[]"); + } catch (e) { + return []; + } + }); + + useEffect(() => { + localStorage.setItem("parts", JSON.stringify(parts)); + }, [parts]); + useEffect(() => { + localStorage.setItem("rules", JSON.stringify(rules)); + }, [rules]); + + // Form states for new part + const [name, setName] = useState(""); + const [type, setType] = useState(""); + const [attrKey, setAttrKey] = useState(""); + const [attrValue, setAttrValue] = useState(""); + const [attrsEditor, setAttrsEditor] = useState([]); // array of {k,v} + + // Rule builder state + const [ruleAType, setRuleAType] = useState(""); + const [ruleAKey, setRuleAKey] = useState(""); + const [ruleOp, setRuleOp] = useState("="); + const [ruleBType, setRuleBType] = useState(""); + const [ruleBKey, setRuleBKey] = useState(""); + + // Compare selections + const [selA, setSelA] = useState(""); + const [selB, setSelB] = useState(""); + const [compatResult, setCompatResult] = useState(null); + + // Helpers + const uniqueTypes = Array.from(new Set(parts.map((p) => p.type))).filter(Boolean); + + function addAttrToEditor() { + if (!attrKey) return; + setAttrsEditor((s) => { + const existing = s.filter((x) => x.k !== attrKey); + return [...existing, { k: attrKey, v: attrValue }]; + }); + setAttrKey(""); + setAttrValue(""); + } + + function removeAttrFromEditor(k) { + setAttrsEditor((s) => s.filter((x) => x.k !== k)); + } + + function addPart() { + if (!name || !type) return alert("Preencha nome e tipo da peça."); + const newPart = { + id: Date.now().toString(), + name, + type, + attrs: attrsEditor.reduce((acc, cur) => ({ ...acc, [cur.k]: cur.v }), {}), + }; + setParts((p) => [newPart, ...p]); + setName(""); + setType(""); + setAttrsEditor([]); + } + + function deletePart(id) { + if (!window.confirm("Excluir esta peça?")) return; + setParts((p) => p.filter((x) => x.id !== id)); + } + + function addRule() { + if (!ruleAType || !ruleAKey || !ruleBType || !ruleBKey) return alert("Preencha todos os campos da regra."); + const newRule = { + id: Date.now().toString(), + a: { type: ruleAType, key: ruleAKey }, + op: ruleOp, + b: { type: ruleBType, key: ruleBKey }, + }; + setRules((r) => [newRule, ...r]); + } + + function deleteRule(id) { + setRules((r) => r.filter((x) => x.id !== id)); + } + + function evaluateRule(rule, partA, partB) { + // Return true if rule satisfied between given parts + const va = partA?.attrs?.[rule.a.key]; + const vb = partB?.attrs?.[rule.b.key]; + switch (rule.op) { + case "=": + return va !== undefined && vb !== undefined && String(va) === String(vb); + case "!=": + return va !== undefined && vb !== undefined && String(va) !== String(vb); + case "in": + // check list membership (comma separated in vb) + if (va === undefined || vb === undefined) return false; + return String(vb).split(",").map(x=>x.trim()).includes(String(va)); + case "contains": + if (va === undefined || vb === undefined) return false; + return String(va).includes(String(vb)) || String(vb).includes(String(va)); + default: + return false; + } + } + + function checkCompatibility(partAId, partBId) { + const pA = parts.find((p) => p.id === partAId); + const pB = parts.find((p) => p.id === partBId); + if (!pA || !pB) { + setCompatResult({ ok: false, message: "Selecione duas peças válidas." }); + return; + } + + // Collect rules relevant between these two types (both directions) + const relevant = rules.filter( + (r) => (r.a.type === pA.type && r.b.type === pB.type) || (r.a.type === pB.type && r.b.type === pA.type) + ); + + // If no rules defined between these types, fallback: compare attributes with same key equality + if (relevant.length === 0) { + const sharedKeys = Object.keys(pA.attrs || {}).filter((k) => k in (pB.attrs || {})); + if (sharedKeys.length === 0) { + setCompatResult({ ok: true, message: "Nenhuma regra definida: nenhum atributo em comum detectado — compatibilidade assumida (verifique manualmente)." }); + return; + } + // require all shared keys to be equal + const allEqual = sharedKeys.every((k) => String(pA.attrs[k]) === String(pB.attrs[k])); + setCompatResult({ ok: allEqual, message: allEqual ? "Compatível por atributos coincidentes." : `Incompatível: atributo(s) divergente(s): ${sharedKeys.filter(k=>String(pA.attrs[k])!==String(pB.attrs[k])).join(", ")}` }); + return; + } + + // Evaluate all relevant rules; for direction-specific, ensure mapping + const results = relevant.map((r) => { + if (r.a.type === pA.type && r.b.type === pB.type) { + return evaluateRule(r, pA, pB); + } else { + // rule defined in opposite direction + return evaluateRule(r, pB, pA); + } + }); + + const ok = results.every(Boolean); + setCompatResult({ ok, message: ok ? "Todas as regras satisfeitas." : "Uma ou mais regras não foram satisfeitas." }); + } + + // CSV import: expected columns: name,type,attr:key,attr:key ... or name,type,key1,key2... where header names become attr keys + function importCSV(text) { + const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean); + if (lines.length === 0) return; + const header = lines[0].split(",").map(h=>h.trim()); + const newParts = []; + for (let i=1;ic.trim()); + const row = {}; + header.forEach((h, idx) => row[h] = cols[idx] ?? ""); + // Build attrs: all headers except name,type become attrs + const attrs = {}; + Object.keys(row).forEach(k => { + if (k !== 'name' && k !== 'type') attrs[k] = row[k]; + }); + newParts.push({ id: Date.now().toString()+"_"+i, name: row['name'] || `part_${i}`, type: row['type'] || 'unknown', attrs }); + } + setParts((p) => [...newParts, ...p]); + } + + function handleCSVUpload(ev) { + const file = ev.target.files?.[0]; + if (!file) return; + const reader = new FileReader(); + reader.onload = (e) => { + importCSV(String(e.target.result || '')); + }; + reader.readAsText(file); + } + + function exportJSON() { + const data = { parts, rules }; + const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'compatibility-db.json'; + a.click(); + URL.revokeObjectURL(url); + } + + function importJSONFile(ev) { + const f = ev.target.files?.[0]; + if (!f) return; + const r = new FileReader(); + r.onload = (e) => { + try { + const obj = JSON.parse(String(e.target.result || '{}')); + if (Array.isArray(obj.parts)) setParts(obj.parts); + if (Array.isArray(obj.rules)) setRules(obj.rules); + } catch (err) { alert('JSON inválido'); } + }; + r.readAsText(f); + } + + function clearAll() { + if (!window.confirm('Apagar tudo (peças + regras)?')) return; + setParts([]); + setRules([]); + } + + return ( +
+

Compatibility App — Protótipo

+
+
+

Cadastrar peça

+
+ + setName(e.target.value)} className="w-full border p-2 rounded" /> + + setType(e.target.value)} className="w-full border p-2 rounded" /> +
+ setAttrKey(e.target.value)} className="border p-2 rounded col-span-1" /> + setAttrValue(e.target.value)} className="border p-2 rounded col-span-1" /> + +
+
+ {attrsEditor.map(a=> ( +
+ {a.k}: {a.v} + +
+ ))} +
+
+ + +
+
+ +
+ +

Importar

+

CSV com cabeçalho: name,type,attr1,attr2...

+ + +

Ou importar/exportar banco JSON

+
+ + +
+ +
+ +
+
+ +
+

Criar regra de compatibilidade

+
+
+ + setRuleAType(e.target.value)} className="w-full border p-2 rounded" placeholder="ex: placa-mãe" /> + + setRuleAKey(e.target.value)} className="w-full border p-2 rounded" placeholder="ex: socket" /> +
+
+ + setRuleBType(e.target.value)} className="w-full border p-2 rounded" placeholder="ex: cpu" /> + + setRuleBKey(e.target.value)} className="w-full border p-2 rounded" placeholder="ex: socket" /> +
+
+
+ + +
+
+ +
+ +
+ +

Regras existentes

+
+ {rules.length === 0 &&

Nenhuma regra criada.

} + {rules.map(r=> ( +
+
+ {r.a.type}.{r.a.key} {r.op} {r.b.type}.{r.b.key} +
+
+ +
+
+ ))} +
+
+
+ +
+

Banco de peças

+
+ {parts.map(p => ( +
+
+
+ {p.name} +
{p.type}
+
+
+ +
+
+
+ {Object.keys(p.attrs || {}).length === 0 &&
Sem atributos
} + {Object.entries(p.attrs || {}).map(([k,v])=> ( +
{k}{v}
+ ))} +
+
+ ))} +
+
+ +
+

Verificar compatibilidade

+
+ + + +
+ {compatResult && ( +
+
{compatResult.ok ? 'Compatível' : 'Incompatível'}
+
{compatResult.message}
+
+ )} + +
+

Notas sobre verificação

+

A verificação: procura regras explícitas entre tipos; se não houver regras, compara atributos com nomes iguais e exige igualdade. Este protótipo é pensada para ser facilmente estendida (ex.: operadores adicionais, avaliação heurística, UI de prioridade de regras).

+
+
+ + +
+ ); +}