├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md └── workflows │ ├── dev-deployment.yml │ └── master-deployment.yml ├── .gitignore ├── .lintstagedrc.json ├── .prettierignore ├── .prettierrc.json ├── CODEOWNERS ├── LICENSE ├── README.md ├── docs ├── README.md ├── antora.yml ├── modules │ └── ROOT │ │ ├── images │ │ ├── Components.png │ │ ├── Components │ │ │ ├── DesktopChatbotDark.png │ │ │ ├── DesktopChatbotLight.png │ │ │ ├── DesktopConnectionModalDark.png │ │ │ ├── DesktopConnectionModalLight.png │ │ │ ├── DesktopHeaderDark.png │ │ │ ├── DesktopHeaderLight.png │ │ │ ├── DesktopPageNotFoundDark.png │ │ │ ├── DesktopPageNotFoundLight.png │ │ │ ├── MobileChatbotDark.png │ │ │ ├── MobileChatbotLight.png │ │ │ └── UserComponent.png │ │ ├── FeaturedImg.jpg │ │ ├── Templates.png │ │ └── Templates │ │ │ ├── CyberSecurityArchitecture.png │ │ │ ├── FeaturedCyberSecurity.png │ │ │ ├── FeaturedEcommerce.png │ │ │ ├── FeaturedFoundation.png │ │ │ ├── FeaturedFoundationMobile.png │ │ │ ├── FeaturedMovie.png │ │ │ ├── FoundationArchitecture.png │ │ │ └── MovieArchitecture.png │ │ ├── nav.adoc │ │ └── pages │ │ ├── Components │ │ ├── Chatbot.adoc │ │ ├── ConnectionModal.adoc │ │ ├── Header.adoc │ │ ├── PageNotFound.adoc │ │ └── User.adoc │ │ ├── Templates │ │ ├── Cybersecurity.adoc │ │ ├── Ecommerce.adoc │ │ ├── Foundation.adoc │ │ └── Movie.adoc │ │ ├── components.adoc │ │ ├── contributing.adoc │ │ ├── examples.adoc │ │ ├── index.adoc │ │ └── quickstart.adoc ├── package.json ├── preview.yml ├── server.js └── yarn.lock ├── index.html ├── package.json ├── postcss.config.js ├── public ├── _redirects ├── assets │ └── movie │ │ ├── movie1.png │ │ ├── movie10.png │ │ ├── movie11.png │ │ ├── movie12.png │ │ ├── movie13.png │ │ ├── movie14.png │ │ ├── movie2.png │ │ ├── movie3.png │ │ ├── movie4.png │ │ ├── movie5.png │ │ ├── movie6.png │ │ ├── movie7.png │ │ ├── movie8.png │ │ └── movie9.png └── vite.svg ├── src ├── App.tsx ├── ConnectionModal.css ├── assets │ └── img │ │ ├── component │ │ ├── ChatbotImg-dark.png │ │ ├── ChatbotImg-light.png │ │ ├── ConnectionModalImg-dark.png │ │ ├── ConnectionModalImg-light.png │ │ ├── HeaderImg-dark.png │ │ └── HeaderImg-light.png │ │ └── template │ │ ├── CyberSecurityImg-dark.png │ │ ├── CyberSecurityImg-light.png │ │ ├── EcommerceImg-dark.png │ │ ├── EcommerceImg-light.png │ │ ├── MovieImg-dark.png │ │ ├── MovieImg-light.png │ │ ├── StarterKitImg-dark.png │ │ └── StarterKitImg-light.png ├── context │ ├── ThemeWrapper.tsx │ └── connectionFile.tsx ├── index.css ├── landingPage │ ├── Content.tsx │ ├── Home.tsx │ ├── categories │ │ ├── Component.tsx │ │ └── Templates.tsx │ └── components │ │ ├── Card.css │ │ └── Card.tsx ├── main.tsx ├── templates │ ├── cybersecurity │ │ ├── CyberSecurity.css │ │ ├── Home.tsx │ │ └── assets │ │ │ └── networkimpact.json │ ├── ecommerce │ │ ├── Content.tsx │ │ ├── Home.tsx │ │ └── assets │ │ │ ├── product1.png │ │ │ ├── product2.png │ │ │ ├── product3.png │ │ │ ├── product4.png │ │ │ ├── product5.png │ │ │ ├── product6.png │ │ │ └── products.json │ ├── foundation │ │ ├── Content.tsx │ │ ├── Home.tsx │ │ └── Layout │ │ │ ├── PageLayout.tsx │ │ │ └── SideNav.tsx │ ├── movie │ │ ├── Content.tsx │ │ ├── Home.tsx │ │ ├── Interfaces.tsx │ │ ├── Movie.tsx │ │ └── assets │ │ │ └── movies.json │ └── shared │ │ ├── assets │ │ ├── ChatbotMessages.json │ │ ├── NoData.png │ │ ├── NotFound.png │ │ ├── chatbot-ai.png │ │ └── chatbot-user.png │ │ ├── components │ │ ├── Chatbot.tsx │ │ ├── ConnectionModal.tsx │ │ ├── Header.tsx │ │ ├── PageNotFound.tsx │ │ └── User.tsx │ │ └── utils │ │ └── Driver.tsx └── vite-env.d.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "plugins": ["@typescript-eslint", "react"], 4 | "extends": ["eslint:recommended", "prettier", "plugin:@typescript-eslint/recommended"], // this is optional 5 | "env": { 6 | "browser": true, 7 | "node": true 8 | }, 9 | "settings": { 10 | "react": { 11 | "version": "detect" 12 | } 13 | }, 14 | "ignorePatterns": ["node_modules/**", "packages/**/dist/**", "packages/**/coverage/**"], 15 | "rules": { 16 | "@typescript-eslint/no-explicit-any": "off", // Off for v1 17 | "@typescript-eslint/ban-ts-comment": "off", // Off for v1 18 | "@typescript-eslint/no-empty-function": "off", // Off for v1 19 | "@typescript-eslint/no-unused-vars": [ 20 | "error", 21 | { "vars": "all", "varsIgnorePattern": "^_*", "args": "after-used", "argsIgnorePattern": "^_" } 22 | ], 23 | "array-callback-return": "off", // Off for v1 24 | "arrow-body-style": "off", 25 | "block-scoped-var": "error", 26 | "camelcase": "off", // Off for v1 27 | "consistent-return": "off", // Off for v1 28 | "consistent-this": ["error", "self"], 29 | "constructor-super": "error", 30 | "curly": ["error", "all"], 31 | "default-case": "error", 32 | "default-param-last": "off", // Off for v1 33 | "dot-notation": "error", 34 | "eqeqeq": "off", // Off for v1 35 | "func-names": "error", 36 | "func-style": [ 37 | "error", 38 | "declaration", 39 | { 40 | "allowArrowFunctions": true 41 | } 42 | ], 43 | "grouped-accessor-pairs": "error", 44 | "line-comment-position": "off", // Off for v1 45 | "lines-between-class-members": "error", 46 | "max-depth": "error", 47 | "max-len": [ 48 | "off", // Off for v1 49 | { 50 | "code": 120, 51 | "comments": 120, 52 | "ignoreUrls": true, 53 | "ignoreTemplateLiterals": true 54 | } 55 | ], 56 | "max-lines-per-function": ["off"], 57 | "max-nested-callbacks": ["error", 5], 58 | "max-statements": ["off"], 59 | "max-statements-per-line": "error", 60 | "no-alert": "off", // Off for v1 61 | "no-array-constructor": "error", 62 | "no-await-in-loop": "off", // Off for v1 63 | "no-buffer-constructor": "error", 64 | "no-caller": "error", 65 | "no-confusing-arrow": "error", 66 | "no-console": "warn", 67 | "no-constructor-return": "error", 68 | "no-constant-condition": "error", 69 | "no-debugger": "warn", 70 | "no-dupe-else-if": "error", 71 | "no-else-return": "error", 72 | "no-empty-function": [ 73 | "off", // Off for v1 74 | { 75 | "allow": ["constructors"] 76 | } 77 | ], 78 | "no-eq-null": "off", // Off for V1 79 | "no-eval": "error", 80 | "no-extend-native": "error", 81 | "no-extra-bind": "error", 82 | "no-extra-label": "error", 83 | "no-implicit-coercion": "error", 84 | "no-implicit-globals": "error", 85 | "no-implied-eval": "error", 86 | "no-import-assign": "error", 87 | "no-invalid-this": "off", 88 | "no-iterator": "error", 89 | "no-labels": "error", 90 | "no-lone-blocks": "error", 91 | "no-lonely-if": "error", 92 | "no-loop-func": "error", 93 | "no-magic-numbers": "off", 94 | "no-multi-assign": "error", 95 | "no-multi-str": "error", 96 | "no-nested-ternary": "off", // Off for v1 97 | "no-new": "error", 98 | "no-new-func": "error", 99 | "no-new-object": "error", 100 | "no-new-wrappers": "error", 101 | "no-octal-escape": "error", 102 | "no-param-reassign": "off", // Off for v1 103 | "no-path-concat": "error", 104 | "no-plusplus": [ 105 | "error", 106 | { 107 | "allowForLoopAfterthoughts": true 108 | } 109 | ], 110 | "no-proto": "off", // Off for v1 111 | "no-restricted-globals": "error", 112 | "no-return-assign": "error", 113 | "no-return-await": "error", 114 | "no-self-compare": "error", 115 | "no-sequences": "error", 116 | "no-setter-return": "error", 117 | "no-sync": "error", 118 | "no-tabs": "error", 119 | "no-template-curly-in-string": "error", 120 | "no-underscore-dangle": "off", // Off for v1 121 | "no-unmodified-loop-condition": "error", 122 | "no-unneeded-ternary": "error", 123 | "no-unreachable": "error", 124 | "no-unused-expressions": "off", // Off for v1 125 | "no-useless-call": "error", 126 | "no-useless-computed-key": "error", 127 | "no-useless-concat": "off", // Off for v1 128 | "no-useless-rename": "error", 129 | "no-useless-return": "error", 130 | "no-var": "error", 131 | "no-void": ["error", { "allowAsStatement": true }], 132 | "one-var": ["error", "never"], 133 | "operator-assignment": "error", 134 | "padding-line-between-statements": "error", 135 | "prefer-arrow-callback": "warn", 136 | "prefer-const": "off", // Off for v1 137 | "prefer-destructuring": [ 138 | // Off for v1 139 | "warn", 140 | { 141 | "VariableDeclarator": { 142 | "array": true, 143 | "object": true 144 | }, 145 | "AssignmentExpression": { 146 | "array": false, 147 | "object": false 148 | } 149 | } 150 | ], 151 | "prefer-numeric-literals": "warn", 152 | "prefer-promise-reject-errors": "warn", 153 | "prefer-rest-params": "warn", 154 | "prefer-spread": "warn", 155 | "prefer-template": "warn", 156 | "radix": "off", // Off for v1 157 | "require-atomic-updates": "error", 158 | "require-await": "warn", // Warn for v1 159 | "sort-keys": "off", 160 | "spaced-comment": [ 161 | "warn", 162 | "always", 163 | { 164 | "markers": ["/"] 165 | } 166 | ], 167 | "symbol-description": "error", 168 | "yoda": "error" 169 | } 170 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Guidelines 11 | Please note that GitHub issues are only meant for bug reports/feature requests. 12 | 13 | 14 | Before creating a new issue, please check whether someone else has raised the same issue. You may be able to add context to that issue instead of duplicating the report. However, each issue should also only be focussed on a _single_ problem, so do not describe new problems within an existing thread - these are very hard to track and manage, and your problem may be ignored. Finally, do not append comments to closed issues; if the same problem re-occurs, open a new issue, and include a link to the old one. 15 | 16 | To help us understand your issue, please specify important details, primarily: 17 | 18 | - Needle StarterKit version: X.Y.Z. 19 | - Neo4j Database version: X.Y.Z (Community/Enterprise/Aura). 20 | - Steps to reproduce 21 | - Expected behavior 22 | - Actual behavior 23 | 24 | Additionally, include (as appropriate) screenshots, drawings, etc. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Guidelines 11 | Please note that GitHub issues are only meant for bug reports/feature requests. 12 | 13 | ## Feature request template 14 | 15 | **Is your feature request related to a problem? Please describe.** 16 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 17 | 18 | **Describe the solution you'd like** 19 | A clear and concise description of what you want to happen. 20 | 21 | **Describe alternatives you've considered** 22 | A clear and concise description of any alternative solutions or features you've considered. 23 | 24 | **Additional context** 25 | Add any other context or screenshots about the feature request here. -------------------------------------------------------------------------------- /.github/workflows/dev-deployment.yml: -------------------------------------------------------------------------------- 1 | name: Test/Deploy Master 2 | 3 | on: 4 | push: 5 | branches: ['development'] 6 | 7 | jobs: 8 | build-test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | node-version: [18.x] 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: yarn install 20 | - name: Eslint check 21 | run: yarn run lint 22 | 23 | deploy-docs: 24 | needs: build-test 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Trigger Developer Event 28 | uses: peter-evans/repository-dispatch@main 29 | with: 30 | token: ${{ secrets.DOCS_REFRESH_TOKEN }} 31 | repository: neo4j-documentation/docs-refresh 32 | event-type: labs 33 | 34 | build-s3: 35 | needs: build-test 36 | runs-on: ubuntu-latest 37 | environment: 38 | name: development 39 | url: https://development.needle-starterkit.graphapp.io/ 40 | strategy: 41 | matrix: 42 | node-version: [ 18.x ] 43 | steps: 44 | - uses: actions/checkout@v4 45 | - name: Use Node.js ${{ matrix.node-version }} 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: ${{ matrix.node-version }} 49 | - run: rm -rf docs 50 | - run: yarn 51 | - run: yarn run build 52 | - uses: kersvers/s3-sync-with-cloudfront-invalidation@v1.0.0 53 | with: 54 | args: --delete 55 | env: 56 | AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID }} 57 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 58 | AWS_S3_BUCKET: ${{ vars.AWS_S3_BUCKET }} 59 | DISTRIBUTION_ID: ${{ vars.AWS_CDN_ID }} 60 | AWS_REGION: 'us-east-1' 61 | SOURCE_DIR: 'dist' -------------------------------------------------------------------------------- /.github/workflows/master-deployment.yml: -------------------------------------------------------------------------------- 1 | name: Test/Deploy Master 2 | 3 | on: 4 | push: 5 | branches: ['[0-9]+.[0-9]+'] 6 | 7 | jobs: 8 | build-test: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | node-version: [18.x] 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | - run: yarn install 20 | - name: Eslint check 21 | run: yarn run lint 22 | 23 | deploy-docs: 24 | needs: build-test 25 | runs-on: ubuntu-latest 26 | steps: 27 | - name: Trigger Developer Event 28 | uses: peter-evans/repository-dispatch@main 29 | with: 30 | token: ${{ secrets.DOCS_REFRESH_TOKEN }} 31 | repository: neo4j-documentation/docs-refresh 32 | event-type: labs 33 | 34 | build-s3: 35 | needs: build-test 36 | runs-on: ubuntu-latest 37 | environment: 38 | name: production 39 | url: https://needle-starterkit.graphapp.io/ 40 | strategy: 41 | matrix: 42 | node-version: [ 18.x ] 43 | steps: 44 | - uses: actions/checkout@v4 45 | - name: Use Node.js ${{ matrix.node-version }} 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: ${{ matrix.node-version }} 49 | - run: rm -rf docs 50 | - run: yarn 51 | - run: yarn run build 52 | - uses: kersvers/s3-sync-with-cloudfront-invalidation@v1.0.0 53 | with: 54 | args: --delete 55 | env: 56 | AWS_ACCESS_KEY_ID: ${{ vars.AWS_ACCESS_KEY_ID }} 57 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 58 | AWS_S3_BUCKET: ${{ vars.AWS_S3_BUCKET }} 59 | DISTRIBUTION_ID: ${{ vars.AWS_CDN_ID }} 60 | AWS_REGION: 'us-east-1' 61 | SOURCE_DIR: 'dist' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | package-lock.json 132 | 133 | docs/build -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.ts": ["prettier --write", "eslint --fix"], 3 | "*.tsx": ["prettier --write", "eslint --fix"], 4 | "*.json": ["prettier --write"], 5 | "*.js": ["prettier --write"] 6 | } -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | docs -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": true, 4 | "singleQuote": true, 5 | "jsxSingleQuote": true, 6 | "useTabs": false, 7 | "tabWidth": 2, 8 | "arrowParens": "always", 9 | "trailingComma": "es5", 10 | "bracketSpacing": true, 11 | "endOfLine": "lf" 12 | } -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | @msenechal -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2023 Niels de Jong 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Needle StarterKit 2 | ![Header](docs/modules/ROOT/images/FeaturedImg.jpg) 3 | 4 | Reactjs Responsive Starter Kit for building applications using [Neo4j Needle](https://www.neo4j.design/) for accelerating TTV. 5 | You can access the [online version here](https://needle-starterkit.graphapp.io). 6 | 7 | ## Features 8 | - 📈 Various templates and components focused on uses-cases and industries 9 | - 🚀 Responsive: Adapts to different screen sizes for optimal user experience. 10 | - 🌚 Dark/Light Mode Theme 🌞: Includes a theme wrapper to switch between light and dark modes without having to reinvent the wheel! 11 | - ⚙️ Neo4j Integration: A simple example for connecting to a Neo4j database. 12 | - 🔐 Neo4j Auto-connect: Automatically connects to the Neo4j database if the user has a session saved (using localStorage). 13 | - 🛠️️ Modular approach: Facilitates easy customization. 14 | 15 | 16 | ## Installation: 17 | ```shell 18 | yarn install 19 | yarn run dev 20 | ``` 21 | 22 | ## Documentation 23 | 24 | The full documentation of every templates and components is available [here](https://neo4j.com/labs/neo4j-needle-starterkit/) 25 | 26 | ## What it looks like 27 | ### Desktop 28 | ![Desktop](/docs/modules/ROOT/images/Templates.png) 29 | ![Desktop](/docs/modules/ROOT/images/Components.png) 30 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Needle Starter Kit Documentation 2 | 3 | This folder contains the documentation for the Needle Starter Kit project. The pages are written in AsciiDoc, and generated into webpages by Antora. 4 | 5 | An external workflow picks up this directory, embeds it into the Neo4j docs, and makes sure generated files are automatically deployed to: 6 | ``` 7 | https://neo4j.com/labs/needle-starter-kit/{version} 8 | ``` 9 | For example: https://neo4j.com/labs/needle-starter-kit/2.0 10 | 11 | ## Local Build 12 | To compile and view the documentation locally, navigate to this (`./docs`) folder and run: 13 | ``` 14 | yarn install 15 | yarn start 16 | ``` 17 | 18 | Then, open your browser and navigate to http://localhost:8000/. -------------------------------------------------------------------------------- /docs/antora.yml: -------------------------------------------------------------------------------- 1 | name: neo4j-needle-starterkit 2 | version: "2.0" 3 | title: Needle Starter Kit 4 | start_page: ROOT:index.adoc 5 | nav: 6 | - modules/ROOT/nav.adoc 7 | 8 | asciidoc: 9 | attributes: 10 | docs-version: "2.0" 11 | page-product: neo4j-needle-starterkit 12 | page-type: Neo4j Needle Starter Kit 13 | page-canonical-root: /labs -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopChatbotDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopChatbotDark.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopChatbotLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopChatbotLight.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopConnectionModalDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopConnectionModalDark.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopConnectionModalLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopConnectionModalLight.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopHeaderDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopHeaderDark.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopHeaderLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopHeaderLight.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopPageNotFoundDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopPageNotFoundDark.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/DesktopPageNotFoundLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/DesktopPageNotFoundLight.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/MobileChatbotDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/MobileChatbotDark.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/MobileChatbotLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/MobileChatbotLight.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Components/UserComponent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Components/UserComponent.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/FeaturedImg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/FeaturedImg.jpg -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/CyberSecurityArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/CyberSecurityArchitecture.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/FeaturedCyberSecurity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/FeaturedCyberSecurity.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/FeaturedEcommerce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/FeaturedEcommerce.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/FeaturedFoundation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/FeaturedFoundation.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/FeaturedFoundationMobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/FeaturedFoundationMobile.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/FeaturedMovie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/FeaturedMovie.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/FoundationArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/FoundationArchitecture.png -------------------------------------------------------------------------------- /docs/modules/ROOT/images/Templates/MovieArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/docs/modules/ROOT/images/Templates/MovieArchitecture.png -------------------------------------------------------------------------------- /docs/modules/ROOT/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:index.adoc[Introduction] 2 | * xref:quickstart.adoc[Quickstart] 3 | * Templates 4 | ** xref:Templates/Foundation.adoc[Foundation] 5 | ** xref:Templates/Cybersecurity.adoc[Cybersecurity] 6 | ** xref:Templates/Movie.adoc[Movie] 7 | ** xref:Templates/Ecommerce.adoc[Ecommerce] 8 | * Components 9 | ** xref:Components/Chatbot.adoc[Chatbot] 10 | ** xref:Components/ConnectionModal.adoc[ConnectionModal] 11 | ** xref:Components/Header.adoc[Header] 12 | ** xref:Components/PageNotFound.adoc[PageNotFound] 13 | ** xref:Components/User.adoc[User] 14 | * xref:contributing.adoc[Contributing] -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Components/Chatbot.adoc: -------------------------------------------------------------------------------- 1 | = Chatbot Component 2 | 3 | [cols="1a,1a"] 4 | |=== 5 | | Desktop Light | Mobile Light 6 | 7 | | image::Components/DesktopChatbotLight.png[DesktopChatbotLight,width=600,height=600] 8 | | image::Components/MobileChatbotLight.png[MobileChatbotLight,width=150,height=150] 9 | |=== 10 | 11 | [cols="1a,1a"] 12 | |=== 13 | | Desktop Dark | Mobile Dark 14 | 15 | | image::Components/DesktopChatbotDark.png[DesktopChatbotDark,width=600,height=600] 16 | | image::Components/MobileChatbotDark.png[MobileChatbotDark,width=150,height=150] 17 | |=== 18 | 19 | The `Chatbot` component provides a dynamic and interactive chat interface for users to communicate with an AI chatbot. It renders chat messages between a user and a chatbot, including features like typing simulation for the chatbot's responses and automatic scrolling to the latest message. 20 | 21 | https://needle-starterkit.graphapp.io/chat-widget-preview[Link to the live preview,window=_blank] 22 | 23 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/shared/components/Chatbot.tsx[Link to the component code,window=_blank] 24 | 25 | == Pre-requisite 26 | 27 | Ensure you have the `@neo4j-ndl` library installed in your project to use this `Chatbot` component. 28 | 29 | == Usage 30 | 31 | To integrate the `Chatbot` component into your application, you will first need to import it: 32 | 33 | [source,tsx] 34 | ---- 35 | import Chatbot from './path/to/Chatbot'; 36 | ---- 37 | 38 | Next, render the `Chatbot` component in your application's component tree, providing the necessary props: 39 | 40 | [source,tsx] 41 | ---- 42 | 43 | ---- 44 | 45 | `listMessages` should be an array of message objects that you want to display initially. 46 | 47 | == Component Props 48 | 49 | The `Chatbot` component accepts the following props: 50 | 51 | [cols="1,2,1"] 52 | |=== 53 | | Name | Description | Required 54 | 55 | | `messages` 56 | | An array of message objects that the chatbot will render. Each message object should contain `id`, `user`, `message`, and `datetime` fields. An optional `isTyping` boolean can also be included to simulate the chatbot typing a response. 57 | | Yes 58 | |=== 59 | 60 | == Message Object Structure 61 | 62 | Each message object in the `messages` array prop should follow this structure: 63 | 64 | [source,typescript] 65 | ---- 66 | { 67 | id: number; // Unique identifier for the message 68 | user: string; // "user" for user messages, "chatbot" for chatbot messages 69 | message: string; // The message text 70 | datetime: string; // Timestamp of the message 71 | isTyping?: boolean; // Optional, simulates typing effect for chatbot messages 72 | } 73 | ---- 74 | 75 | == Key components 76 | 77 | 78 | === Handling State and Effects 79 | 80 | The component uses React's `useState` hook to manage: 81 | 82 | - `listMessages`: The current list of messages to be displayed. 83 | - `inputMessage`: The current text input by the user. 84 | 85 | It also uses the `useEffect` hook to automatically scroll to the bottom of the message list whenever a new message is added thansk to the `messagesEndRef` reference. 86 | 87 | === Submitting Messages 88 | 89 | When a user submits a message: 90 | 91 | 1. It prevents the default form submission behavior. 92 | 2. It checks if the message is not empty. 93 | 3. It adds the user's message to the `listMessages` state. 94 | 4. It clears the input field. 95 | 5. It simulates a chatbot response using `simulateTypingEffect`. 96 | 97 | === Simulating Typing Effect 98 | 99 | The `simulateTypingEffect` function simulates the chatbot typing a response, displaying one character at a time. Once the message is fully "typed out," it updates the message to indicate the chatbot has finished typing. 100 | 101 | == Example 102 | 103 | Here is a basic example of using the `Chatbot` component with initial messages: 104 | 105 | [source,tsx] 106 | ---- 107 | const listMessages = [ 108 | { 109 | id: 1, 110 | user: 'user', 111 | message: 'Hello, chatbot!', 112 | datetime: '01/01/2024 00:00:00', 113 | }, 114 | { 115 | id: 2, 116 | user: 'chatbot', 117 | message: 'Hello! How can I assist you today?', 118 | datetime: '01/01/2024 00:00:00', 119 | }, 120 | ]; 121 | 122 | 123 | ---- 124 | 125 | This will render a chat interface with two initial messages, one from the user and one from the chatbot. 126 | 127 | == Component Integration 128 | 129 | Integrating the `Chatbot` component into an existing application is straightforward. Make sure to provide it with the necessary `messages` prop to initialize the chat history. The component handles user inputs and chatbot responses internally, offering a complete chat interface experience out of the box. 130 | 131 | Here is an example if you already have a backend application taking care of generating the chatbot's responses and you want to integrate it with this `Chatbot` component: 132 | 133 | First, we will set a new state `gettingResponse` that will indicate us if we are currently fetching a response from the backend: 134 | 135 | [source, tsx] 136 | ---- 137 | const [gettingResponse, setGettingResponse] = useState(false); 138 | ---- 139 | 140 | Then, we will define a new function `fetchResponseFromAPI` that will be responsible for fetching the chatbot's response from the backend based on the user's message: 141 | 142 | [source, tsx] 143 | ---- 144 | const fetchResponseFromAPI = async () => { 145 | setGettingResponse(true); 146 | const requestBody = { 147 | message: inputMessage 148 | }; 149 | 150 | try { 151 | const response = await fetch(``, { 152 | method: 'POST', 153 | headers: { 154 | 'accept': 'application/json', 155 | 'Content-Type': 'application/json', 156 | }, 157 | body: JSON.stringify(requestBody), 158 | }); 159 | const data = await response.json(); 160 | setGettingResponse(false); 161 | return data.content; 162 | } catch (error) { 163 | console.error("API call failed:", error); 164 | return "Sorry, something went wrong."; 165 | } finally { 166 | setGettingResponse(false); 167 | } 168 | }; 169 | ---- 170 | 171 | WARNING: Ideally you will want to consider using a framework to manage the states, caching and hooks like `tanstack/react-query` for example as well as adding an authentication and authorization to your backend API calls 172 | 173 | Then all we need to do is to call this function when the user submits a message, retrieve the response, and simulate the typing effect: 174 | In our `handleSubmit` function: 175 | 176 | [source, tsx] 177 | ---- 178 | const chatbotReply = await fetchResponseFromAPI(); 179 | simulateTypingEffect(chatbotReply); 180 | ---- 181 | 182 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/development-openaichatbot/src/templates/shared/components/Chatbot.tsx[The full example can be found here,window=_blank] -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Components/ConnectionModal.adoc: -------------------------------------------------------------------------------- 1 | = ConnectionModal Component 2 | 3 | [cols="1a,1a"] 4 | |=== 5 | | Light | Dark 6 | 7 | | image::Components/DesktopConnectionModalLight.png[DesktopConnectionModalLight,width=600,height=600] 8 | | image::Components/DesktopConnectionModalDark.png[DesktopConnectionModalDark,width=600,height=600] 9 | |=== 10 | 11 | The ConnectionModal component is a React component designed to handle the connection setup to a Neo4j database. It provides a user-friendly interface for entering database connection details and displays feedback on the connection attempt. This component is particularly useful in applications requiring a dynamic connection to a Neo4j database. 12 | 13 | https://needle-starterkit.graphapp.io/connection-modal-preview[Link to the live preview,window=_blank] 14 | 15 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/shared/components/ConnectionModal.tsx[Link to the component code,window=_blank] 16 | 17 | The connection modal also comes with a dropzone for the user to upload a file containing the connection details. The file can be either an Aura credential file you downloaded when creating an instance, or your own file containing the connection details. 18 | 19 | The accepted file format are either `.env` or `.txt` file with the following structure: 20 | 21 | [cols="1,2,1"] 22 | |=== 23 | | Name | Description | Example 24 | 25 | | `NEO4J_URI` 26 | | The URI of the Neo4j database. Ideally you would have the protocol, hostname and port, but all are optional and will be defaulted if not present. 27 | | neo4j+s://abcd1234.databases.neo4j.io:7687 28 | | `NEO4J_USERNAME` 29 | | Your neo4j username 30 | | neo4j 31 | | `NEO4J_PASSWORD` 32 | | Your neo4j password 33 | | password 34 | | `NEO4J_DATABASE` 35 | | The database name 36 | | neo4j 37 | |=== 38 | 39 | Example of a `local.env` file: 40 | [source,env] 41 | ---- 42 | NEO4J_URI=neo4j://localhost:7687 43 | NEO4J_USERNAME=neo4j 44 | NEO4J_PASSWORD=password 45 | NEO4J_DATABASE=neo4j 46 | ---- 47 | 48 | == Pre-requisite 49 | 50 | - Ensure you have the `@neo4j-ndl` library installed in your project to use this `ConnectionModal` component. 51 | - Ensure you also import the utils/Driver.tsx as it is used for creating the driver connection to the Neo4j Database. 52 | 53 | == Usage 54 | 55 | To integrate the ConnectionModal component into your application, follow these steps: 56 | 57 | 1. Import the component: 58 | 59 | [source,jsx] 60 | ---- 61 | import ConnectionModal from './path/to/ConnectionModal'; 62 | ---- 63 | 64 | 2. Add state to your parent component to control the modal's visibility and to handle the connection status: 65 | 66 | [source,jsx] 67 | ---- 68 | const [isModalOpen, setIsModalOpen] = useState(false); 69 | const [connectionStatus, setConnectionStatus] = useState(false); 70 | ---- 71 | 72 | 3. Render the `ConnectionModal` component with the required props: 73 | 74 | [source,jsx] 75 | ---- 76 | 82 | ---- 83 | 84 | == Component Props 85 | 86 | The ConnectionModal component accepts the following props: 87 | 88 | [cols="1,2,1"] 89 | |=== 90 | | Name | Description | Required 91 | 92 | | `open` 93 | | A boolean state indicating whether the modal is open or closed. 94 | | Yes 95 | | `setOpenConnection` 96 | | A function to update the `open` state above. 97 | | Yes 98 | | `setConnectionStatus` 99 | | A function to update the parent component's connection status based on the success or failure of the connection attempt. 100 | | Yes 101 | | `message?` 102 | | An optional `Message` object containing a `type` for styling the banner (success, info, warning, danger, neutral) and a `content` string for the message text. 103 | | No 104 | |=== 105 | 106 | `(Optionnal)` Message object structure: 107 | 108 | [source,typescript] 109 | ---- 110 | { 111 | type: string; // success | info | warning | danger | neutral 112 | content: string; // The message content 113 | } 114 | ---- 115 | 116 | == Key components 117 | 118 | === Handling State 119 | 120 | The component uses the `useState` hook to manage local state for each connection parameter (protocol, hostname, port, etc.) and the connection message to provide feedback. 121 | 122 | === Submitting the Connection Details 123 | 124 | Upon submitting the form: 125 | 126 | 1. It constructs the connection URI using the selected protocol, hostname, and port. 127 | 2. It calls `setDriver` to attempt the database connection. 128 | 3. Based on the success or failure of `setDriver`, it updates the connection status and potentially closes the modal or displays an error message. 129 | 130 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Components/Header.adoc: -------------------------------------------------------------------------------- 1 | = Header Component 2 | 3 | [cols="1a,1a"] 4 | |=== 5 | | Light | Dark 6 | 7 | | image::Components/DesktopHeaderLight.png[DesktopHeaderLight,width=600,height=600] 8 | | image::Components/DesktopHeaderDark.png[DesktopHeaderDark,width=600,height=600] 9 | |=== 10 | 11 | The Header component serves as the primary navigation and interaction header in a web application. It incorporates theme toggling, navigation tabs, and optional Neo4j database connection functionality. 12 | 13 | https://needle-starterkit.graphapp.io/header-preview[Link to the live preview,window=_blank] 14 | 15 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/shared/components/Header.tsx[Link to the component code,window=_blank] 16 | 17 | == Pre-requisite 18 | 19 | - Ensure you have the `@neo4j-ndl` library installed in your project to use this `Header` component. 20 | - Ensure you also import the User.tsx as it is used for displaying a user profile. 21 | 22 | == Usage 23 | 24 | To use the Header component in your application, follow these steps: 25 | 26 | 1. Import the component: 27 | 28 | [source,jsx] 29 | ---- 30 | import Header from './path/to/Header'; 31 | ---- 32 | 33 | 2. `Optional` Define the navigation items and state management logic for active navigation items and Neo4j connection status: 34 | 35 | [source,jsx] 36 | ---- 37 | const navItems = ['Home', 'About', 'Contact']; 38 | const [activeNavItem, setActiveNavItem] = useState(navItems[0]); 39 | const [connectNeo4j, setConnectNeo4j] = useState(false); 40 | const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); 41 | ---- 42 | 43 | 3. Render the `Header` component with the required props: 44 | 45 | [source,jsx] 46 | ---- 47 | 48 | // 49 |
setIsConnectionModalOpen(true)} 58 | userHeader={true} 59 | /> 60 | ---- 61 | 62 | == Component Props 63 | 64 | The Header component accepts several props for customization and functionality: 65 | 66 | [cols="1,2,1"] 67 | |=== 68 | | Name | Description | Required 69 | 70 | | `title` 71 | | The title/Name of your application, will be displayed in the header. 72 | | Yes 73 | | `navItems` 74 | | The list of navigation items. 75 | | No 76 | | `activeNavItem` 77 | | The navigation item currently active/selected. 78 | | No 79 | | `setActiveNavItem` 80 | | Function to set the active navigation item. 81 | | No 82 | | `useNeo4jConnect` 83 | | Boolean to enable or disable the Neo4j connection feature. 84 | | No 85 | | `connectNeo4j` 86 | | If `useNeo4jConnect` is set to true - Boolean indicating the current connection status to a Neo4j database. 87 | | No 88 | | `setConnectNeo4j` 89 | | If `useNeo4jConnect` is set to true - Function to update the Neo4j connection status. 90 | | No 91 | | `openConnectionModal` 92 | | If `useNeo4jConnect` is set to true - Function to open the connection modal for Neo4j. 93 | | No 94 | | `userHeader` 95 | | Boolean to display or hide the user section in the header. 96 | | No 97 | |=== 98 | 99 | 100 | == Key components 101 | 102 | === Theme Toggling 103 | 104 | The header includes a theme toggle switch, allowing users to switch between light and dark modes. It uses the `ThemeWrapperContext` to access and modify the theme state across the application. 105 | 106 | [source, tsx] 107 | ---- 108 | const themeUtils = React.useContext(ThemeWrapperContext); 109 | const [themeMode, setThemeMode] = useState(themeUtils.colorMode); 110 | 111 | const toggleColorMode = () => { 112 | setThemeMode((prevThemeMode) => { 113 | return prevThemeMode === 'light' ? 'dark' : 'light'; 114 | }); 115 | themeUtils.toggleColorMode(); 116 | }; 117 | 118 | // ... 119 | 120 | 121 | {themeMode === 'dark' ? ( 122 | 123 | 124 | 125 | ) : ( 126 | 127 | 128 | 129 | )} 130 | 131 | ---- 132 | 133 | === Navigation Tabs 134 | 135 | Navigation is handled using the `Tabs` component from `@neo4j-ndl/react`, with each `navItem` rendered as a tab. The active navigation item is highlighted, and changing tabs updates the application's state accordingly. 136 | 137 | [source, tsx] 138 | ---- 139 |
140 | setActiveNavItem(e)} value={activeNavItem}> 141 | {navItems.map((item) => ( 142 | 143 | {item} 144 | 145 | ))} 146 | 147 |
148 | ---- 149 | 150 | === Neo4j Connection 151 | 152 | If `useNeo4jConnect` is true, a `Switch` component controls the connection to a Neo4j database. Toggling this switch can trigger a modal for connecting to the database, managed by the `openConnectionModal` prop function. 153 | 154 | [source, tsx] 155 | ---- 156 | {useNeo4jConnect ? ( 157 | { 160 | if (e.target.checked) { 161 | openConnectionModal(); 162 | } else { 163 | setConnectNeo4j(false); 164 | } 165 | }} 166 | disabled={false} 167 | fluid={true} 168 | label={`Connect${connectNeo4j ? 'ed' : ''} to Neo4j`} 169 | labelBefore={true} 170 | /> 171 | ) : null} 172 | ---- 173 | 174 | === User Section 175 | 176 | An optional user section can be included, rendering a `User` component if `userHeader` is true. This section is designed for user-related actions or information, such as login status or user settings. 177 | 178 | [source, tsx] 179 | ---- 180 | {userHeader ? ( 181 |
182 | 183 |
184 | ) : null} 185 | ---- 186 | 187 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Components/PageNotFound.adoc: -------------------------------------------------------------------------------- 1 | = PageNotFoundPage Component 2 | 3 | [cols="1a,1a"] 4 | |=== 5 | | Light | Dark 6 | 7 | | image::Components/DesktopPageNotFoundLight.png[DesktopPageNotFoundLight,width=600,height=600] 8 | | image::Components/DesktopPageNotFoundDark.png[DesktopPageNotFoundDark,width=600,height=600] 9 | |=== 10 | 11 | The `PageNotFoundPage` component is a React component designed to display a user-friendly message when a page is not found (404 error). It provides a visual indication that the requested page is unavailable and offers a button to navigate back to the previous page. 12 | 13 | https://needle-starterkit.graphapp.io/notfound[Link to the live preview,window=_blank] 14 | 15 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/shared/components/PageNotFound.tsx[Link to the component code,window=_blank] 16 | 17 | == Pre-requisite 18 | 19 | - Ensure you have the `@neo4j-ndl` library installed in your project to use this `PageNotFound` component. 20 | 21 | == Usage 22 | 23 | To integrate the `PageNotFoundPage` component into your application, ensure it is part of your routing configuration: 24 | 25 | 1. Import the component: 26 | 27 | [source,jsx] 28 | ---- 29 | import PageNotFoundPage from './path/to/PageNotFoundPage'; 30 | ---- 31 | 32 | 2. Add a route for the component in your router setup. This is typically done using a catch-all route: 33 | 34 | [source,jsx] 35 | ---- 36 | import { Routes, Route } from 'react-router-dom'; 37 | 38 | 39 | // ... All your other routes MUST be before this one 40 | } /> 41 | 42 | ---- 43 | 44 | == Component Props 45 | 46 | The `PageNotFoundPage` component does not accept any props. 47 | 48 | == Key components 49 | 50 | === Navigation Hook 51 | 52 | The component uses the `useNavigate` hook to programmatically navigate the user back to the previous page: 53 | 54 | [source,jsx] 55 | ---- 56 | const navigate = useNavigate(); 57 | 58 | 59 | ---- 60 | 61 | This makes use of the browser's history stack to return the user to where they came from. 62 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Components/User.adoc: -------------------------------------------------------------------------------- 1 | = User Component 2 | 3 | image::Components/UserComponent.png[UserComponent,width=300,height=300] 4 | 5 | 6 | The User component provides a user interface for displaying user information and a dropdown menu for user actions such as 'Profile' and 'Logout'. 7 | 8 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/shared/components/User.tsx[Link to the component code,window=_blank] 9 | 10 | == Pre-requisite 11 | 12 | - Ensure you have the `@neo4j-ndl` library installed in your project to use this `User` component. 13 | 14 | == Usage 15 | 16 | To use the User component in your application, you can directly import and place it within your component tree: 17 | 18 | [source,jsx] 19 | ---- 20 | import User from './path/to/User'; 21 | 22 | // In your component's render method or functional component return statement 23 | 24 | ---- 25 | 26 | == Component Props 27 | 28 | The User component does not accept any props. 29 | 30 | == Key components 31 | 32 | === State Management 33 | 34 | - `anchorEl`: Used to manage the anchor element for the dropdown menu. It determines the position of the dropdown. 35 | - `open`: A derived state from `anchorEl` to control the visibility of the dropdown menu. 36 | 37 | === Event Handling 38 | 39 | - `handleClick`: Sets the `anchorEl` state to the current event target, effectively opening the dropdown menu. 40 | - `handleClose`: Resets the `anchorEl` state to `null`, closing the dropdown menu. 41 | - `menuSelect`: Handles the selection of a menu item. It currently displays an alert with the selected item's name and closes the menu. 42 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Templates/Cybersecurity.adoc: -------------------------------------------------------------------------------- 1 | = Cybersecurity 2 | 3 | image::Templates/FeaturedCyberSecurity.png[Cybersecurity,align="center"] 4 | 5 | == Introduction 6 | 7 | The Cybersecurity dashboard is an interface designed to provide a comprehensive overview of network impacts, illustrating potential vulnerabilities and system statuses within a network infrastructure. This tool leverages a dynamic data visualization approach, rendering interactive charts, graphs, and tables. 8 | 9 | https://needle-starterkit.graphapp.io/cybersecurity-preview[Link to the live preview,window=_blank] 10 | 11 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/cybersecurity[Link to the template code,window=_blank] 12 | 13 | == Documentation 14 | 15 | === Layout Architecture 16 | 17 | image::Templates/CyberSecurityArchitecture.png[CyberSecurityArchitecture,align="center"] 18 | 19 | The template is structured around a main `Home.tsx` component that encapsulates the entire dashboard's functionality. It includes a dynamic search bar, a tabbed interface for switching between table and graph views, and modals for establishing connections to a Neo4j database. 20 | 21 | === Code Snippets 22 | 23 | .Here is a snippet demonstrating the React table setup using `@tanstack/react-table`: 24 | 25 | [source,tsx] 26 | ---- 27 | const columns = [ 28 | columnHelper.accessor('Type', { 29 | header: () => Type, 30 | cell: (info) => info.getValue(), 31 | footer: (info) => info.column.id, 32 | }), 33 | // Additional columns defined here... 34 | ]; 35 | 36 | const table = useReactTable({ 37 | data, 38 | columns, 39 | enableSorting: true, 40 | getSortedRowModel: getSortedRowModel(), 41 | getCoreRowModel: getCoreRowModel(), 42 | }); 43 | ---- 44 | 45 | === Dataset/Connect to a Neo4j DB 46 | 47 | The application utilizes a static JSON dataset named `networkimpact.json` for demonstration purposes. It is not yet designed to connect to a Neo4j database for dynamic data retrieval. However, you can use the `ConnectionModal` component for connecting to a Neo4j Database and implement the logic to retrieve your data into the table in place of the static JSON this template is currently using. 48 | 49 | .Connection to Neo4j Database: 50 | 51 | [source,tsx] 52 | ---- 53 | 58 | ---- 59 | 60 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Templates/Ecommerce.adoc: -------------------------------------------------------------------------------- 1 | = E-commerce 2 | 3 | image::Templates/FeaturedEcommerce.png[E-commerce,align="center"] 4 | 5 | == Introduction 6 | 7 | The E-commerce platform showcases a dynamic and interactive way to display products, their details, and similar or frequently bought together items. This template aim at introducing and explaining how the recommendation engine use case can be leveraged for the retail industry. 8 | 9 | https://needle-starterkit.graphapp.io/ecommerce-preview[Link to the live preview,window=_blank] 10 | 11 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/ecommerce[Link to the template code,window=_blank] 12 | 13 | == Documentation 14 | 15 | === Layout Architecture 16 | 17 | The template's core is split into two main components: `Home.tsx` and `Content.tsx`. The `Home` component serves as the entry point, rendering the header and the content area, which includes detailed product information, similar products, and bundle deals. 18 | 19 | The layout is designed to be both intuitive and visually appealing, ensuring users can easily navigate through product offerings and access detailed descriptions seamlessly. 20 | 21 | === Code Snippets 22 | 23 | .Here is a snippet showcasing the use of `@tanstack/react-table` for product specifications: 24 | 25 | [source,tsx] 26 | ---- 27 | const columns = [ 28 | columnHelper.accessor('CPU', { 29 | cell: (info) => info.getValue(), 30 | footer: (info) => info.column.id, 31 | }), 32 | // Additional columns... 33 | ]; 34 | 35 | const table = useReactTable({ 36 | data, 37 | columns, 38 | enableSorting: true, 39 | getSortedRowModel: getSortedRowModel(), 40 | getCoreRowModel: getCoreRowModel(), 41 | }); 42 | ---- 43 | 44 | .This snippet demonstrates rendering of the featured product: 45 | 46 | [source,tsx] 47 | ---- 48 |
49 | Product 1 50 |
51 | {products[0].desc1} 52 |
53 | 56 |
57 |
58 | DealOfTheWeek 59 | // Additional tags... 60 |
61 |
62 |
63 | ---- 64 | 65 | .This snippet demonstrates rendering of the similar products: 66 | 67 | [source,tsx] 68 | ---- 69 |
70 | Similar products 71 |
72 | {[productImg2, productImg3, productImg4].map((img, index) => ( 73 | 74 |
75 | {`Product 76 |
77 |
{products[index + 1].desc1}
78 | Price: £{products[index + 1].price} 79 | 80 |
81 |
82 |
83 | ))} 84 |
85 |
86 | ---- 87 | 88 | .This snippet demonstrates rendering of the frequently bought together product: 89 | 90 | [source,tsx] 91 | ---- 92 |
93 | Frequently bought together 94 |
95 | {[productImg5, productImg6].map((img, index) => ( 96 | 97 |
98 | Product 1 99 | 100 | 101 | 102 | {`Product 103 |
104 | 105 | Package deal 106 | 107 | 108 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat, quasi? Pariatur ipsam voluptatum, 109 | quas labore amet dolor dolore, aspernatur tempora quasi ullam ad, autem distinctio doloribus! Iusto 110 | rem iste accusamus. 111 | 112 |
113 | ))} 114 |
115 |
116 | ---- 117 | 118 | === Dataset/Product Catalog 119 | 120 | The application initially uses a static JSON dataset named `products.json` for the product catalog. This dataset contains an array of product objects, each with detailed information such as name, price, description, and specifications. 121 | 122 | The template supports easy integration with dynamic data sources. Although this is a static template, you can further extend it for real-time inventory updates, user reviews, personalized recommendations, and much more. 123 | 124 | .Data Representation: 125 | 126 | [source,json] 127 | ---- 128 | { 129 | "listProducts": [ 130 | { 131 | "id": 1, 132 | "name": "Product 1", 133 | "price": 999.99, 134 | "desc1": "Lorem ipsum dolor sit amet...", 135 | // Additional product details... 136 | }, 137 | // Additional products... 138 | ] 139 | } 140 | ---- 141 | 142 | This template provides a foundation for developing a comprehensive recommendation engine in the retail industry. 143 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Templates/Foundation.adoc: -------------------------------------------------------------------------------- 1 | = Foundation Template 2 | 3 | [cols="1a,1a"] 4 | |=== 5 | | Desktop | Mobile 6 | 7 | | image::Templates/FeaturedFoundation.png[FeaturedFoundation,width=600,height=600] 8 | | image::Templates/FeaturedFoundationMobile.png[FeaturedFoundationMobile,width=150,height=150] 9 | |=== 10 | 11 | == Introduction 12 | 13 | The Foundation template provides a generic, simple yet powerful template for developing applications on top of your Neo4j databases. It features a clean and intuitive layout, including a side navigation bar, a central content area, and a connection modal for connecting or disconnecting from Neo4j instances. 14 | 15 | https://needle-starterkit.graphapp.io/foundation-preview[Link to the live preview,window=_blank] 16 | 17 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/foundation[Link to the template code,window=_blank] 18 | 19 | == Pre-requisite 20 | 21 | - Ensure you have the `@neo4j-ndl` library installed in your project to use this `Foundation` template. 22 | 23 | This template is using the following components and utilities from the shared folders, make sure to import them: 24 | 25 | - The Header component from the shared component folder. 26 | - The ConnectionModal component from the shared component folder. 27 | - The Driver utility from the shared utils folder. 28 | 29 | == Documentation 30 | 31 | === Layout Architecture 32 | 33 | image::Templates/FoundationArchitecture.png[FoundationArchitecture,align="center"] 34 | 35 | The template structure is centered around the `Home.tsx` component, which incorporates the `Header` and `PageLayout` components. `PageLayout` further divides the page into `SideNav` and `Content` sections, offering a comprehensive environment for application development. 36 | 37 | The layout is designed to be responsive, ensuring a seamless user experience across various devices and screen sizes. 38 | 39 | === Code Snippets 40 | 41 | .Here is a snippet for the auto-connect feature in `Content.tsx` 42 | 43 | [source,tsx] 44 | ---- 45 | useEffect(() => { 46 | if (!init) { 47 | let session = localStorage.getItem('needleStarterKit-neo4j.connection'); 48 | if (session) { 49 | let neo4jConnection = JSON.parse(session); 50 | setDriver(neo4jConnection.uri, neo4jConnection.user, neo4jConnection.password) 51 | .then((isSuccessful: boolean) => { 52 | setConnectionStatus(isSuccessful); 53 | }); 54 | } 55 | setInit(true); 56 | } 57 | }); 58 | ---- 59 | 60 | .This snippet showcases the side navigation bar setup in `SideNav.tsx` 61 | 62 | [source,tsx] 63 | ---- 64 | 65 | 66 | } 71 | > 72 | Search 73 | 74 | // Additional navigation items... 75 | 76 | 77 | ---- 78 | 79 | === Integration with Neo4j 80 | 81 | The StarterKit is designed to seamlessly integrate with Neo4j databases, facilitating easy connectivity and interaction with Neo4j. The `ConnectionModal` component enables users to connect to or disconnect from their Neo4j instances, providing error feedback on the connection status if it does not succeed to connect. 82 | 83 | .Connection Modal Usage: 84 | 85 | [source,tsx] 86 | ---- 87 | 92 | ---- -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/Templates/Movie.adoc: -------------------------------------------------------------------------------- 1 | = Movie Recommendations 2 | 3 | image::Templates/FeaturedMovie.png[Movie Recommendations,align="center"] 4 | 5 | == Introduction 6 | 7 | The Movie Recommendations template is an interactive platform designed to suggest movies based on users' preferences and viewing habits. It aim to act as a strong foundation for building your recommendation engine and provide personalized movie suggestions. 8 | 9 | This template is the only one (`currently`) providing not only a static dataset but also allow you to connect to your Neo4j Database to retrieve your data and render it in the template. 10 | 11 | CAUTION: This feature allowing you to connect and retrieve data from your database into the template is experimental (hence only available in this template). It is designed to work with the https://github.com/neo4j-graph-examples/recommendations[Recommendation dataset,window=_blank]. If you are using a different dataset or data model, you may need to modify the cypher queries and/or the data structure to fit your needs. 12 | 13 | https://needle-starterkit.graphapp.io/movie-preview[Link to the live preview,window=_blank] 14 | 15 | https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/2.0/src/templates/movie[Link to the template code,window=_blank] 16 | 17 | == Documentation 18 | 19 | === Layout Architecture 20 | 21 | image::Templates/MovieArchitecture.png[MovieArchitecture,align="center"] 22 | 23 | The template entry is in the `Home.tsx` component, which orchestrates the user interactions and data retrieval from the static JSON file or your Neo4j database. 24 | 25 | Key components include: 26 | 27 | - `Header` for navigation and database connection management. 28 | - `ConnectionModal` for handling database connection configurations. 29 | - `Content` for presenting movie recommendations through interactive UI elements. 30 | 31 | === Code Snippets 32 | 33 | .Fetching and Displaying Recommendations 34 | 35 | [source,tsx] 36 | ---- 37 | useEffect(() => { 38 | const fetchData = async () => { 39 | setLoading({ main: true, similarGenre: true, otherUsers: true }); 40 | if (useReco) { 41 | const { uri, user, password } = JSON.parse(localStorage.getItem('neo4j-connection') ?? '') ?? {}; 42 | setDriver(uri, user, password); 43 | await Promise.all([ 44 | fetchMovies(queryMainMovie).then(movies => setMainMovie(movies || [])), 45 | fetchMovies(similarByGenre).then(movies => setRecoSimilarGenre(movies || [])), 46 | fetchMovies(queryOtherUsersAlsoWatched).then(movies => setRecoOtherUsers(movies || [])), 47 | ]); 48 | setLoading({ main: false, similarGenre: false, otherUsers: false }); 49 | } 50 | }; 51 | 52 | fetchData(); 53 | }, [useReco]); 54 | ---- 55 | 56 | .Displaying Error Messages 57 | 58 | [source,tsx] 59 | ---- 60 | {recoError ? ( 61 | 62 | Data error 63 | 64 | An error occurred while fetching recommendations data. Please ensure you are connected to a Neo4j Database. 65 | 66 | 67 | ) : ( 68 | 69 | )} 70 | ---- 71 | 72 | === Dataset/Connect to a Neo4j DB 73 | 74 | By default, the application uses a static dataset `movies.json` for demonstration purposes. The template supports dynamic switching between the static dataset and live data from your Neo4j database to give you a more real view of what the template could look like with your data. 75 | 76 | .Connection to Neo4j Database: 77 | 78 | [source,tsx] 79 | ---- 80 | 89 | ---- 90 | 91 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/components.adoc: -------------------------------------------------------------------------------- 1 | 2 | # Components 3 | 4 | The Needle Starterkit makes use of the official https://storybook-components-build.appspot.com/?path=/docs/getting-started-welcome--docs[Neo4j Needle,window=_blank] React component library. 5 | You can use these components together with the higher-order components part of the starter kit. 6 | 7 | ## Architecture 8 | The default template consists of the following higher level components: 9 | 10 | image::ComponentArchitecture.png[Component Architecture] 11 | 12 | 13 | - The **Header** is the title bar of the page, containing universal components irrespective of the current page's scope. 14 | - A **Logo** for the application. Neo4j's logo is used by default. 15 | - **User** details for the currently connected user. 16 | - Application-level **Settings**, that are global to the entire app. (e.g. switching between light/dark mode) 17 | - A dynamic **PageLayout**, which can change based on the user's current view / scope. 18 | - A **SideNav** that lets the user switch between different application contexts. 19 | - The page **Content** containing the key functionality for the current context. 20 | 21 | ## Additional Components 22 | 23 | An example **Connection Modal** is available under `src/components/ConnectionModal.tsx`. If your application requires a (direct) Neo4j connection, this modal can be re-used to let users sign in to their Neo4j databases. 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/contributing.adoc: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions to the project are highly welcomed. 4 | If you extend the starter kit with your own (reusable) templates/components, consider creating a Pull Request. Ensure you start from the latest release branch, and set the merge base to this branch as well. 5 | 6 | For your feature to be accepted, ensure: 7 | 8 | - There is a matching https://github.com/neo4j-labs/neo4j-needle-starterkit/issues[issue,window=_blank] on GitHub. 9 | - Your code is formatted and linted correctly. (See `yarn run lint`). 10 | - The template/component is well documented in the documentation portal (if applicable). 11 | 12 | 13 | ## Feature Requests / Bugs 14 | If you have a request for a feature, or have found a bug, creating an https://github.com/neo4j-labs/neo4j-needle-starterkit/issues[issue on GitHub,window=_blank] is the best way to reach out. -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/examples.adoc: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## Getting started 4 | As an example to get you started, here are the steps to retrieve and display data from a Neo4j database for the Movie graph. 5 | 6 | ### Retrieve data 7 | First, to retrieve data, we can create a function that will return the data we need in the `src/utils/Driver.tsx` file. 8 | In this example, we will focus on getting all the actors for the movie `The Matrix`. 9 | 10 | .Driver.tsx 11 | [source, typescript] 12 | ---- 13 | export async function getMatrixActors() { 14 | const session = driver.session(); 15 | const result = await session.run( 16 | 'MATCH (actor:Person)-[:ACTED_IN]-(movie:Movie {title: "The Matrix"}) RETURN actor.name AS actorName' 17 | ); 18 | session.close(); 19 | let listActors : string[] = []; 20 | result.records.map((record) => ( 21 | listActors.push(record.get('actorName')) 22 | )); 23 | return listActors; 24 | } 25 | ---- 26 | 27 | In this function, we execute a cypher query to retrieve all the actors that acted in the movie `The Matrix`, and return their name. 28 | We then return an array, containing all the actors' names. 29 | 30 | ### Display data 31 | 32 | To display the data, we will have to update the `src/components/Content.tsx` file. 33 | 34 | .Content.tsx 35 | [source, typescript] 36 | ---- 37 | ... 38 | import { setDriver, disconnect, getMatrixActors } from '../utils/Driver'; // <1> 39 | 40 | export default function Content() { 41 | ... 42 | const [listActors, setListActors] = useState>([]); // <2> 43 | 44 | ... 45 | // <3> 46 | function retrieveMatrixActors() { 47 | getMatrixActors().then((actors) => { 48 | setListActors(actors); 49 | }); 50 | }; 51 | 52 | ... 53 | // <4> 54 | {!connectionStatus ? ( 55 | 56 | ) : ( 57 |
58 | 59 |
60 | The Matrix actors: 61 |
62 | {listActors.length > 0 ? ( 63 |
    64 | {listActors.map((actor) => ( 65 |
  • {actor}
  • 66 | ))} 67 |
68 | ) : ( 69 | No actors found 70 | )} 71 |
72 |
73 | 74 |
75 | )} 76 | ... 77 | 78 | ---- 79 | 80 | <1> Import our new function `getMatrixActors` in the existing import statement. 81 | <2> Create a new state variable to store the list of actors we will retrieve from the database. 82 | <3> Create a button click handler that will call our `getMatrixActors` function and update the state variable with the result when we click. 83 | <4> Display the data in a simple list. 84 | 85 | ## Templates 86 | This page will contain a set of example applications built using the Needle starter kit. 87 | Looking for inspiration on what to build? Check out https://neo4j.com/developer-blog/needle-neo4j-design-system/[this post,window=_blank] on the Neo4j Developer Blog. 88 | 89 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/index.adoc: -------------------------------------------------------------------------------- 1 | # Neo4j Needle StarterKit 2 | 3 | Reactjs Responsive Starter Kit for building applications using https://www.neo4j.design/[Neo4j Needle,window=_blank] for accelerating TTV of your Neo4j-powered front-end applications. 4 | You can access the https://needle-starterkit.graphapp.io[online version here,window=_blank]. 5 | 6 | image::FeaturedImg.jpg[Featured Image] 7 | 8 | 9 | ## Features 10 | - 📈 Various templates and components focused on specific uses-cases and industries 11 | - 🚀 Responsive: Adapts to different screen sizes for optimal user experience. 12 | - 🌚 Dark/Light Mode Theme 🌞: Includes a theme wrapper to switch between light and dark modes without having to reinvent the wheel! 13 | - ⚙️ Neo4j Integration: A simple example for connecting to a Neo4j database. 14 | - 🔐 Neo4j Auto-connect: Automatically connects to the Neo4j database if the user has a session saved (using localStorage). 15 | - 🛠️️ Modular approach: Facilitates easy customization. 16 | 17 | 18 | ## Documentation 19 | 20 | - To get started, see the link:quickstart[Quickstart] page. 21 | - For details on the available templates, see the Templates section. 22 | - For details on the available components, see the Components section. 23 | - Examples and documentation are available for every templates and components. 24 | - If you want to contribute to this project, see link:contributing[Contributing]. 25 | 26 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/quickstart.adoc: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | If you want to try it without installing it locally, you can access https://needle-starterkit.graphapp.io[our self-hosted,window=_blank] version. 4 | 5 | Alternatively, if you want to run it locally or within your self-managed environment, please follow the steps below. 6 | 7 | ## Prerequisites 8 | Running the StarterKit requires you to have **node.js** and **yarn** installed. 9 | 10 | If you do not have these installed: 11 | 12 | 1. https://nodejs.org/en/download[Install node.js,window=_blank] for your operating system. The node installation will include `npm`. 13 | 2. Use `npm` to install `yarn` as described https://classic.yarnpkg.com/lang/en/docs/install/[here,window=_blank]. 14 | 15 | 16 | ## Install & Run 17 | Download the latest version of the starter kit from the https://github.com/neo4j-labs/neo4j-needle-starterkit[Github Repository,window=_blank]: 18 | 19 | 20 | ```shell 21 | git clone git@github.com:neo4j-labs/neo4j-needle-starterkit.git 22 | ``` 23 | 24 | Navigate to the folder you just cloned and run the following commands to install and run the development server: 25 | 26 | ```shell 27 | cd neo4j-needle-starterkit 28 | yarn install 29 | yarn run dev 30 | ``` 31 | 32 | If all is successful, you will be shown the following message: 33 | 34 | ```shell 35 | VITE v4.5.1 ready in 125 ms 36 | 37 | ➜ Local: http://localhost:3000/ 38 | ➜ Network: use --host to expose 39 | ➜ press h to show help 40 | ``` 41 | 42 | ## Next steps 43 | After you have the StarterKit app up-and-running, you can explore the different templates and components (see the relevant section for further details and documentation) or implement your own custom components, templates and logic. 44 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "needle-starter-kit-docs", 3 | "version": "2.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "npm run dev", 9 | "dev": "node server.js & npm-watch preview", 10 | "preview": "antora preview.yml", 11 | "publish": "git push origin HEAD:publish" 12 | }, 13 | "watch": { 14 | "preview": { 15 | "patterns": [ 16 | "modules" 17 | ], 18 | "extensions": "adoc" 19 | } 20 | }, 21 | "dependencies": { 22 | "@antora/cli": "^3.1.1", 23 | "@antora/site-generator-default": "^3.1.1", 24 | "@neo4j-documentation/macros": "^1.0.0", 25 | "@neo4j-documentation/remote-include": "^1.0.0", 26 | "express": "^4.17.1", 27 | "npm-watch": "^0.11.0" 28 | } 29 | } -------------------------------------------------------------------------------- /docs/preview.yml: -------------------------------------------------------------------------------- 1 | site: 2 | title: Needle Starter Kit 3 | 4 | content: 5 | sources: 6 | - url: ../ 7 | start_path: docs 8 | branches: HEAD 9 | exclude: 10 | - '!**/_includes/*' 11 | - '!**/readme.adoc' 12 | - '!**/README.adoc' 13 | ui: 14 | bundle: 15 | url: https://static-content.neo4j.com/build/ui-bundle-latest.zip 16 | snapshot: true 17 | urls: 18 | html_extension_style: indexify 19 | asciidoc: 20 | extensions: 21 | - "@neo4j-documentation/remote-include" 22 | - "@neo4j-documentation/macros" 23 | attributes: 24 | page-theme: labs -------------------------------------------------------------------------------- /docs/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const app = express() 4 | const version = "2.0" 5 | app.use(express.static('./build/site')) 6 | app.get('/', (req, res) => res.redirect('neo4j-needle-starterkit/' + version)) 7 | app.listen(8000, () => console.log('📘 http://localhost:8000')) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Neo4j Needle Starterkit 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neo4j-needle-starterkit", 3 | "private": true, 4 | "version": "2.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "format": "prettier --write \"**/*.{ts,tsx}\"", 10 | "lint": "eslint --ext .ts --ext .tsx . --report-unused-disable-directives --max-warnings 0", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@neo4j-ndl/base": "2.6.0", 15 | "@neo4j-ndl/react": "2.6.2", 16 | "@tanstack/react-table": "^8.9.3", 17 | "autoprefixer": "^10.4.17", 18 | "eslint-plugin-react": "^7.33.2", 19 | "neo4j-driver": "^5.14.0", 20 | "postcss": "^8.4.33", 21 | "react": "^18.2.0", 22 | "react-dom": "^18.2.0", 23 | "react-router-dom": "^6.21.3", 24 | "tailwindcss": "^3.4.1", 25 | "uuid": "^9.0.1" 26 | }, 27 | "devDependencies": { 28 | "@types/node": "^20.11.16", 29 | "@types/react": "^18.2.15", 30 | "@types/react-dom": "^18.2.7", 31 | "@typescript-eslint/eslint-plugin": "^6.0.0", 32 | "@typescript-eslint/parser": "^6.0.0", 33 | "@vitejs/plugin-react": "^4.0.3", 34 | "eslint": "^8.45.0", 35 | "eslint-config-prettier": "^8.5.0", 36 | "eslint-plugin-react-hooks": "^4.6.0", 37 | "eslint-plugin-react-refresh": "^0.4.3", 38 | "prettier": "^2.7.1", 39 | "typescript": "5.3.3", 40 | "vite": "^4.4.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/assets/movie/movie1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie1.png -------------------------------------------------------------------------------- /public/assets/movie/movie10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie10.png -------------------------------------------------------------------------------- /public/assets/movie/movie11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie11.png -------------------------------------------------------------------------------- /public/assets/movie/movie12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie12.png -------------------------------------------------------------------------------- /public/assets/movie/movie13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie13.png -------------------------------------------------------------------------------- /public/assets/movie/movie14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie14.png -------------------------------------------------------------------------------- /public/assets/movie/movie2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie2.png -------------------------------------------------------------------------------- /public/assets/movie/movie3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie3.png -------------------------------------------------------------------------------- /public/assets/movie/movie4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie4.png -------------------------------------------------------------------------------- /public/assets/movie/movie5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie5.png -------------------------------------------------------------------------------- /public/assets/movie/movie6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie6.png -------------------------------------------------------------------------------- /public/assets/movie/movie7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie7.png -------------------------------------------------------------------------------- /public/assets/movie/movie8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie8.png -------------------------------------------------------------------------------- /public/assets/movie/movie9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/public/assets/movie/movie9.png -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 3 | 4 | import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; 5 | 6 | import ThemeWrapper from './context/ThemeWrapper'; 7 | 8 | import Home from './landingPage/Home'; 9 | import PageNotFound from './templates/shared/components/PageNotFound'; 10 | 11 | import Foundation from './templates/foundation/Home'; 12 | import Cybersecurity from './templates/cybersecurity/Home'; 13 | import Movie from './templates/movie/Home'; 14 | import ECommerce from './templates/ecommerce/Home'; 15 | 16 | import Chatbot from './templates/shared/components/Chatbot'; 17 | import messagesData from './templates/shared/assets/ChatbotMessages.json'; 18 | import ConnectionModal from './templates/shared/components/ConnectionModal'; 19 | import Header from './templates/shared/components/Header'; 20 | import User from './templates/shared/components/User'; 21 | 22 | import { FileContextProvider } from './context/connectionFile'; 23 | 24 | import './ConnectionModal.css'; 25 | 26 | function App() { 27 | const messages = messagesData.listMessages; 28 | const [activeTab, setActiveTab] = useState('Home'); 29 | return ( 30 | 31 | 32 | 33 | } /> 34 | } /> 35 | } /> 36 | } /> 37 | } /> 38 | 42 | null} setConnectionStatus={() => null} /> 43 | 44 | } 45 | /> 46 | } /> 47 | 57 | } 58 | /> 59 | } /> 60 | } /> 61 | 62 | 63 | 64 | ); 65 | } 66 | 67 | export default App; 68 | -------------------------------------------------------------------------------- /src/ConnectionModal.css: -------------------------------------------------------------------------------- 1 | .ndl-dropzone-header{ 2 | display: flex!important; 3 | flex-direction: row!important; 4 | gap: 30px!important; 5 | } -------------------------------------------------------------------------------- /src/assets/img/component/ChatbotImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/component/ChatbotImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/component/ChatbotImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/component/ChatbotImg-light.png -------------------------------------------------------------------------------- /src/assets/img/component/ConnectionModalImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/component/ConnectionModalImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/component/ConnectionModalImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/component/ConnectionModalImg-light.png -------------------------------------------------------------------------------- /src/assets/img/component/HeaderImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/component/HeaderImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/component/HeaderImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/component/HeaderImg-light.png -------------------------------------------------------------------------------- /src/assets/img/template/CyberSecurityImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/CyberSecurityImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/template/CyberSecurityImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/CyberSecurityImg-light.png -------------------------------------------------------------------------------- /src/assets/img/template/EcommerceImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/EcommerceImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/template/EcommerceImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/EcommerceImg-light.png -------------------------------------------------------------------------------- /src/assets/img/template/MovieImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/MovieImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/template/MovieImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/MovieImg-light.png -------------------------------------------------------------------------------- /src/assets/img/template/StarterKitImg-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/StarterKitImg-dark.png -------------------------------------------------------------------------------- /src/assets/img/template/StarterKitImg-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/assets/img/template/StarterKitImg-light.png -------------------------------------------------------------------------------- /src/context/ThemeWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useMemo, useState, ReactNode } from 'react'; 2 | import { NeedleThemeProvider, useMediaQuery } from '@neo4j-ndl/react'; 3 | 4 | export const ThemeWrapperContext = createContext({ 5 | toggleColorMode: () => {}, 6 | colorMode: 'light' as string, 7 | }); 8 | 9 | interface ThemeWrapperProps { 10 | children: ReactNode; 11 | } 12 | 13 | export default function ThemeWrapper({ children }: ThemeWrapperProps) { 14 | const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); 15 | const [mode, setMode] = useState(prefersDarkMode ? 'dark' : 'light'); 16 | const [usingPreferredMode, setUsingPreferredMode] = useState(true); 17 | const themeWrapperUtils = useMemo( 18 | () => ({ 19 | colorMode: mode, 20 | toggleColorMode: () => { 21 | setMode((prevMode) => { 22 | setUsingPreferredMode(false); 23 | themeBodyInjection(prevMode); 24 | return prevMode === 'light' ? 'dark' : 'light'; 25 | }); 26 | }, 27 | }), 28 | [mode] 29 | ); 30 | const themeBodyInjection = (mode: string) => { 31 | if (mode === 'light') { 32 | document.body.classList.add('ndl-theme-dark'); 33 | } else { 34 | document.body.classList.remove('ndl-theme-dark'); 35 | } 36 | }; 37 | 38 | if (usingPreferredMode) { 39 | prefersDarkMode ? themeBodyInjection('light') : themeBodyInjection('dark'); 40 | } 41 | 42 | return ( 43 | 44 | 45 | {children} 46 | 47 | 48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/context/connectionFile.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useState, ReactNode, Dispatch, SetStateAction } from 'react'; 2 | 3 | interface FileContextType { 4 | file: File[] | []; 5 | setFile: Dispatch>; 6 | } 7 | const FileContext = createContext(undefined); 8 | interface FileContextProviderProps { 9 | children: ReactNode; 10 | } 11 | const FileContextProvider: React.FC = ({ children }) => { 12 | const [file, setFile] = useState([]); 13 | const value: FileContextType = { 14 | file, 15 | setFile, 16 | }; 17 | return {children}; 18 | }; 19 | const useFileContext = () => { 20 | const context = useContext(FileContext); 21 | if (!context) { 22 | throw new Error('useFileContext must be used within a FileContextProvider'); 23 | } 24 | return context; 25 | }; 26 | export { FileContextProvider, useFileContext }; 27 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* src/index.css */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | body { 7 | margin: 0; 8 | } -------------------------------------------------------------------------------- /src/landingPage/Content.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '@neo4j-ndl/react'; 2 | import Templates from './categories/Templates'; 3 | import Component from './categories/Component'; 4 | 5 | export default function Content({ activeTab }: { activeTab: string }) { 6 | return ( 7 |
8 | 9 | {activeTab === 'Template' ? : activeTab === 'Component' ? : <>} 10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/landingPage/Home.tsx: -------------------------------------------------------------------------------- 1 | import Header from '../templates/shared/components/Header'; 2 | import { useState } from 'react'; 3 | import Content from './Content'; 4 | 5 | export default function QuickStarter() { 6 | const [activeTab, setActiveTab] = useState('Template'); 7 | 8 | return ( 9 |
10 |
18 |
19 | 20 |
21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/landingPage/categories/Component.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '@neo4j-ndl/react'; 2 | import Card from '../components/Card'; 3 | 4 | // Dark mode featured images 5 | import ChatbotImgDark from '../../assets/img/component/ChatbotImg-dark.png'; 6 | import ConnectionModalImgDark from '../../assets/img/component/ConnectionModalImg-dark.png'; 7 | import HeaderImgDark from '../../assets/img/component/HeaderImg-dark.png'; 8 | 9 | // Light mode featured images 10 | import ChatbotImgLight from '../../assets/img/component/ChatbotImg-light.png'; 11 | import ConnectionModalImgLight from '../../assets/img/component/ConnectionModalImg-light.png'; 12 | import HeaderImgLight from '../../assets/img/component/HeaderImg-light.png'; 13 | 14 | import { useContext } from 'react'; 15 | import { ThemeWrapperContext } from '../../context/ThemeWrapper'; 16 | 17 | export default function Component() { 18 | const { colorMode } = useContext(ThemeWrapperContext); 19 | 20 | const componentCards = [ 21 | { 22 | title: 'Chatbot Widget', 23 | description: 24 | 'An interactive chat widget component which you can use for your support, sales or any other chatbot needs. It is designed to be easily embeddable and customizable to fit your needs', 25 | image: colorMode === 'dark' ? ChatbotImgDark : ChatbotImgLight, 26 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 27 | import.meta.env.PACKAGE_VERSION 28 | }/src/templates/shared/components/Chatbot.tsx`, 29 | previewLink: '/chat-widget-preview', 30 | }, 31 | { 32 | title: 'Connection Modal', 33 | description: 34 | 'A responsive and user-friendly connection modal template, ideal for facilitating connection to your Neo4j databases in your applications.', 35 | image: colorMode === 'dark' ? ConnectionModalImgDark : ConnectionModalImgLight, 36 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 37 | import.meta.env.PACKAGE_VERSION 38 | }/src/templates/shared/components/ConnectionModal.tsx`, 39 | previewLink: '/connection-modal-preview', 40 | }, 41 | { 42 | title: 'Header', 43 | description: 44 | 'A modern and clean header component, perfect for any web application. It is designed to be easily embeddable and customizable to fit your needs.', 45 | image: colorMode === 'dark' ? HeaderImgDark : HeaderImgLight, 46 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 47 | import.meta.env.PACKAGE_VERSION 48 | }/src/templates/shared/components/Header.tsx`, 49 | previewLink: '/header-preview', 50 | }, 51 | ]; 52 | 53 | return ( 54 |
55 | 56 | Components 57 | 58 | 59 | Our components (patterns) are perfect for those seeking to integrate individual widgets or elements into their 60 | existing projects. These templates range from chatbot to a connections modal and many more coming on the way, 61 | offering a versatile selection of standalone components. Each component is built to be easily adaptable and 62 | embeddable, ensuring seamless integration with your larger project. 63 | 64 |
65 | {componentCards.map((card, index) => ( 66 |
67 | 74 |
75 | ))} 76 |
77 |
78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /src/landingPage/categories/Templates.tsx: -------------------------------------------------------------------------------- 1 | import { Typography } from '@neo4j-ndl/react'; 2 | import Card from '../components/Card'; 3 | 4 | // Dark mode featured images 5 | import StarterKitImgDark from '../../assets/img/template/StarterKitImg-dark.png'; 6 | import EcommerceImgDark from '../../assets/img/template/EcommerceImg-dark.png'; 7 | import MovieImgDark from '../../assets/img/template/MovieImg-dark.png'; 8 | import CyberSecurityImgDark from '../../assets/img/template/CyberSecurityImg-dark.png'; 9 | 10 | // Light mode featured images 11 | import StarterKitImgLight from '../../assets/img/template/StarterKitImg-light.png'; 12 | import EcommerceImgLight from '../../assets/img/template/EcommerceImg-light.png'; 13 | import MovieImgLight from '../../assets/img/template/MovieImg-light.png'; 14 | import CyberSecurityImgLight from '../../assets/img/template/CyberSecurityImg-light.png'; 15 | 16 | import { useContext } from 'react'; 17 | import { ThemeWrapperContext } from '../../context/ThemeWrapper'; 18 | 19 | export default function Templates() { 20 | const { colorMode } = useContext(ThemeWrapperContext); 21 | 22 | const templatesCards = [ 23 | { 24 | title: 'Foundation Template', 25 | description: 26 | 'The Foundation template, because we all starts somewhere, this was the first template we created, combining simple, modern and UX all together for a generic application design.', 27 | image: colorMode === 'dark' ? StarterKitImgDark : StarterKitImgLight, 28 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 29 | import.meta.env.PACKAGE_VERSION 30 | }/src/templates/foundation`, 31 | previewLink: '/foundation-preview', 32 | }, 33 | { 34 | title: 'Cyber security', 35 | description: 36 | 'Explore your network infrastructure and retrieve impact analysis in one click! This templates facilitate the visualization and analysis of complex infrastructures.', 37 | image: colorMode === 'dark' ? CyberSecurityImgDark : CyberSecurityImgLight, 38 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 39 | import.meta.env.PACKAGE_VERSION 40 | }/src/templates/cybersecurity`, 41 | previewLink: '/cybersecurity-preview', 42 | }, 43 | { 44 | title: 'Movie Recommendation', 45 | description: 'Simple and modern movie recommendation template based on the famous Neo4j movie dataset.', 46 | image: colorMode === 'dark' ? MovieImgDark : MovieImgLight, 47 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 48 | import.meta.env.PACKAGE_VERSION 49 | }/src/templates/movie`, 50 | previewLink: '/movie-preview', 51 | }, 52 | { 53 | title: 'Ecommerce', 54 | description: 55 | 'E-commerce product page with a modern and clean design. Perfect for having one featured product followed by suggestions on recommended products and packages/frequently bought together.', 56 | image: colorMode === 'dark' ? EcommerceImgDark : EcommerceImgLight, 57 | sourceCode: `https://github.com/neo4j-labs/neo4j-needle-starterkit/blob/${ 58 | import.meta.env.PACKAGE_VERSION 59 | }/src/templates/ecommerce`, 60 | previewLink: '/ecommerce-preview', 61 | }, 62 | ]; 63 | 64 | return ( 65 |
66 | 67 | Templates 68 | 69 | 70 | Dive into our collection of ready-to-use templates tailored for various use cases and industries. These 71 | templates are designed to serve a wide range of product purposes. Our templates provide a seamless blend of 72 | aesthetics and utility, ensuring your product not only meets its functional objectives but also delivers a 73 | memorable user experience. 74 | 75 |
76 | {templatesCards.map((card, index) => ( 77 |
78 | 85 |
86 | ))} 87 |
88 |
89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /src/landingPage/components/Card.css: -------------------------------------------------------------------------------- 1 | .image-link:hover .image { 2 | filter: blur(8px); 3 | } 4 | .image-link:hover .overlay-text { 5 | opacity: 1; 6 | z-index: 2; 7 | } 8 | .image { 9 | transition: filter 0.3s; 10 | } 11 | .overlay-text { 12 | position: absolute; 13 | top: 0; 14 | left: 0; 15 | right: 0; 16 | bottom: 0; 17 | width: 100%; 18 | height: 100%; 19 | display: flex; 20 | align-items: center; 21 | justify-content: center; 22 | color: white; 23 | background-color: rgba(0,0,0,0.5); 24 | opacity: 0; 25 | transition: opacity 0.3s; 26 | font-size: 20px; 27 | z-index: 1; 28 | } -------------------------------------------------------------------------------- /src/landingPage/components/Card.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Typography, Widget } from '@neo4j-ndl/react'; 2 | import { CodeBracketIconOutline, EyeIconOutline } from '@neo4j-ndl/react/icons'; 3 | import { Link } from 'react-router-dom'; 4 | import './Card.css'; 5 | 6 | export default function Card({ 7 | title, 8 | description, 9 | image, 10 | sourceCode, 11 | previewLink, 12 | }: { 13 | title: string; 14 | description: string; 15 | image: string; 16 | sourceCode: string; 17 | previewLink: string; 18 | }) { 19 | return ( 20 |
21 | 22 |
23 | 36 | 37 |
38 |
39 | {title} 40 |
41 | {description} 42 |
43 |
44 |
45 | 46 | 49 | 50 |
51 |
52 |
53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.tsx'; 4 | import './index.css'; 5 | import '@neo4j-ndl/base/lib/neo4j-ds-styles.css'; 6 | 7 | ReactDOM.createRoot(document.getElementById('root')!).render( 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /src/templates/cybersecurity/CyberSecurity.css: -------------------------------------------------------------------------------- 1 | .landing-page { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | height: 100vh; 7 | } 8 | 9 | .search-bar { 10 | transition: all 0.5s ease; 11 | display: flex; 12 | justify-content: center; 13 | gap: 8px; 14 | } 15 | 16 | .search-bar.center { 17 | width: 100dvh; 18 | position: absolute; 19 | top: 50%; 20 | transform: translateY(-50%); 21 | font-size: 1.5rem; 22 | } 23 | 24 | .search-bar.top { 25 | position: absolute; 26 | top: 80px; 27 | font-size: 1rem; 28 | } 29 | 30 | .results-div { 31 | margin-top: 20vh; 32 | font-size: 1.2rem; 33 | text-align: center; 34 | } 35 | 36 | .text-input-container { 37 | transition: width 1.5s ease; 38 | width: 100dvh; 39 | } 40 | 41 | .text-input-container.search-initiated { 42 | width: 60dvh; 43 | } 44 | 45 | .search-result { 46 | visibility: hidden; 47 | opacity: 0; 48 | } 49 | 50 | .search-result-visible { 51 | visibility: visible; 52 | opacity: 1; 53 | transform: translateY(0); 54 | transition: opacity 1.5s ease, transform 1.5s ease; 55 | } 56 | -------------------------------------------------------------------------------- /src/templates/cybersecurity/Home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { 3 | DataGrid, 4 | Flex, 5 | IconButton, 6 | StatusIndicator, 7 | Tabs, 8 | TextInput, 9 | Tip, 10 | Typography, 11 | Widget, 12 | } from '@neo4j-ndl/react'; 13 | import { MagnifyingGlassIconOutline, InformationCircleIconOutline } from '@neo4j-ndl/react/icons'; 14 | import { createColumnHelper, getCoreRowModel, getSortedRowModel, useReactTable } from '@tanstack/react-table'; 15 | 16 | import productsData from './assets/networkimpact.json'; 17 | import NoGraphImg from '../shared/assets/NoData.png'; 18 | import Header from '../shared/components/Header'; 19 | 20 | import './CyberSecurity.css'; 21 | 22 | type NetworkImpact = { 23 | Type: string; 24 | Name: string; 25 | Version: string; 26 | Status: string; 27 | }; 28 | 29 | const columnHelper = createColumnHelper(); 30 | 31 | const columns = [ 32 | columnHelper.accessor('Type', { 33 | header: () => Type, 34 | cell: (info) => info.getValue(), 35 | footer: (info) => info.column.id, 36 | }), 37 | columnHelper.accessor('Name', { 38 | header: () => Name, 39 | cell: (info) => info.getValue(), 40 | footer: (info) => info.column.id, 41 | }), 42 | columnHelper.accessor('Version', { 43 | header: () => Version, 44 | cell: (info) => {info.renderValue()}, 45 | footer: (info) => info.column.id, 46 | }), 47 | columnHelper.accessor('Status', { 48 | header: () => Status, 49 | cell: (info) => ( 50 | <> 51 | 52 | {info.getValue().startsWith('CVE') ? ( 53 |
54 | CVE Detected 55 | 56 | 57 | 58 | 59 | 60 | {info.renderValue()} 61 | 62 | 63 |
64 | ) : ( 65 | <>{info.renderValue()} 66 | )} 67 | 68 | ), 69 | footer: (info) => info.column.id, 70 | }), 71 | ]; 72 | 73 | export default function Home() { 74 | const [searchQuery, setSearchQuery] = useState(''); 75 | const [isSearchInitiated, setIsSearchInitiated] = useState(false); 76 | const network = productsData.ListItems; 77 | const defaultData: NetworkImpact[] = network as NetworkImpact[]; 78 | const [data] = React.useState(() => [...defaultData]); 79 | 80 | const [activeTab, setActiveTab] = useState(0); 81 | 82 | const handleSearch = (e: { preventDefault: () => void }) => { 83 | e.preventDefault(); 84 | setIsSearchInitiated(true); 85 | }; 86 | 87 | const table = useReactTable({ 88 | data, 89 | columns, 90 | enableSorting: true, 91 | getSortedRowModel: getSortedRowModel(), 92 | getCoreRowModel: getCoreRowModel(), 93 | }); 94 | 95 | const displayResult = () => { 96 | if (isSearchInitiated) { 97 | const searchResultElement = document.getElementById('search-result'); 98 | if (searchResultElement) { 99 | searchResultElement.classList.add('search-result-visible'); 100 | } 101 | } 102 | }; 103 | 104 | return ( 105 | <> 106 |
107 | 108 |
109 |
110 |
114 | setSearchQuery(e.target.value)} 118 | placeholder='Search...' 119 | helpText='Search for server, IP, domain, etc.' 120 | fluid={true} 121 | rightIcon={ 122 | 123 | 124 | 125 | } 126 | /> 127 |
128 |
129 | 135 | 136 |
137 | setActiveTab(e)} value={activeTab}> 138 | Table 139 | Graph 140 | 141 | 142 | {activeTab === 0 ? ( 143 | 144 | isResizable={false} 145 | tableInstance={table} 146 | isKeyboardNavigable={false} 147 | styling={{ 148 | zebraStriping: true, 149 | borderStyle: 'none', 150 | headerStyle: 'filled', 151 | }} 152 | components={{ 153 | Navigation: null, 154 | }} 155 | /> 156 | ) : ( 157 | 158 | 159 | 160 | Graph Screen 161 |
162 | 163 | This is where you would display a graph of the network/search result. 164 | 165 | 166 | For now, we do not ship any visualization library to let you the freedom of using the one you 167 | prefer.
168 | although, that might change in the futur... 169 |
170 | <> 171 | 172 | If you want to use visualization libraries, here are few ones that you can use on top of Neo4j 173 | (in no specific order): 174 |
    175 |
  • Neovis.js
  • 176 |
  • D3.js
  • 177 |
  • react-force-graph
  • 178 |
  • Cytoscape.js
  • 179 |
180 |
181 |
182 |
183 |
184 | )} 185 |
186 |
187 |
188 | <>Results for "{searchQuery}" 189 |
190 |
191 |
192 |
193 | 194 | ); 195 | } 196 | -------------------------------------------------------------------------------- /src/templates/cybersecurity/assets/networkimpact.json: -------------------------------------------------------------------------------- 1 | { 2 | "ListItems": [ 3 | { 4 | "Type": "Linux", 5 | "Name": "nginx-edge", 6 | "Version": "Debian 11.2", 7 | "Status": "Up" 8 | }, 9 | { 10 | "Type": "Linux", 11 | "Name": "db-mysql01", 12 | "Version": "Ubuntu 20.04.3 LTS", 13 | "Status": "Down" 14 | }, 15 | { 16 | "Type": "Windows", 17 | "Name": "windev2022", 18 | "Version": "Windows Server 2022 - b21H2", 19 | "Status": "Needs updates" 20 | }, 21 | { 22 | "Type": "Linux", 23 | "Name": "cache-redis", 24 | "Version": "Alpine Linux 3.14", 25 | "Status": "CVE-2022-1921" 26 | }, 27 | { 28 | "Type": "Linux", 29 | "Name": "build-jenkins", 30 | "Version": "CentOS 8", 31 | "Status": "Up" 32 | }, 33 | { 34 | "Type": "Windows", 35 | "Name": "ad-primary", 36 | "Version": "Windows Server 2016 - b1607", 37 | "Status": "Up" 38 | }, 39 | { 40 | "Type": "Linux", 41 | "Name": "monitoring-grafana", 42 | "Version": "Fedora 34", 43 | "Status": "Down" 44 | }, 45 | { 46 | "Type": "Linux", 47 | "Name": "backup-veeam", 48 | "Version": "RHEL 8.4", 49 | "Status": "Up" 50 | }, 51 | { 52 | "Type": "Windows", 53 | "Name": "fileserver01", 54 | "Version": "Windows Server 2019 - b1809", 55 | "Status": "Up" 56 | }, 57 | { 58 | "Type": "Linux", 59 | "Name": "prod-k8s-master", 60 | "Version": "Ubuntu 22.04 LTS", 61 | "Status": "Needs updates" 62 | }, 63 | { 64 | "Type": "Linux", 65 | "Name": "dev-docker01", 66 | "Version": "CentOS 7.9", 67 | "Status": "Needs updates" 68 | }, 69 | { 70 | "Type": "Windows", 71 | "Name": "sqlserver-prod", 72 | "Version": "Windows Server 2019 - b1809", 73 | "Status": "Up" 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /src/templates/ecommerce/Content.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, DataGrid, Typography, Widget, Tag } from '@neo4j-ndl/react'; 3 | import { PlusCircleIconOutline } from '@neo4j-ndl/react/icons'; 4 | import { createColumnHelper, getCoreRowModel, getSortedRowModel, useReactTable } from '@tanstack/react-table'; 5 | 6 | import productsData from './assets/products.json'; 7 | import productImg1 from './assets/product1.png'; 8 | import productImg2 from './assets/product2.png'; 9 | import productImg3 from './assets/product3.png'; 10 | import productImg4 from './assets/product4.png'; 11 | import productImg5 from './assets/product5.png'; 12 | import productImg6 from './assets/product6.png'; 13 | 14 | type Product = { 15 | CPU: number; 16 | Memory: string; 17 | Details: string; 18 | }; 19 | 20 | const columnHelper = createColumnHelper(); 21 | 22 | const columns = [ 23 | columnHelper.accessor('CPU', { 24 | cell: (info) => info.getValue(), 25 | footer: (info) => info.column.id, 26 | }), 27 | columnHelper.accessor('Memory', { 28 | cell: (info) => info.getValue(), 29 | footer: (info) => info.column.id, 30 | }), 31 | columnHelper.accessor('Details', { 32 | cell: (info) => info.getValue(), 33 | footer: (info) => info.column.id, 34 | }), 35 | ]; 36 | 37 | export default function Content() { 38 | const products = productsData.listProducts; 39 | const defaultData: Product[] = products[0].defaultData as Product[]; 40 | const [data] = React.useState(() => [...defaultData]); 41 | 42 | const table = useReactTable({ 43 | data, 44 | columns, 45 | enableSorting: true, 46 | getSortedRowModel: getSortedRowModel(), 47 | getCoreRowModel: getCoreRowModel(), 48 | }); 49 | 50 | return ( 51 |
52 |
53 | {/* Featured Product */} 54 | {products[0].name} 55 |
56 | Product 1 57 |
58 |
59 | {products[0].desc1} 60 | 61 | {products[0].desc2} 62 | 63 |
64 | 77 |
78 |
79 | DealOfTheWeek 80 | In Stock 81 | Next day delivery 82 |
83 |
84 |
85 | Price: £{products[0].price} 86 | 87 |
88 |
89 |
90 | 91 | {/* Similar Products */} 92 |
93 | Similar products 94 |
95 | {[productImg2, productImg3, productImg4].map((img, index) => ( 96 | 97 |
98 | {`Product 99 |
100 |
{products[index + 1].desc1}
101 | Price: £{products[index + 1].price} 102 | 103 |
104 |
105 |
106 | ))} 107 |
108 |
109 | 110 | {/* Frequently Bought Together */} 111 |
112 | Frequently bought together 113 |
114 | {[productImg5, productImg6].map((img, index) => ( 115 | 116 |
117 | Product 1 118 | 119 | 120 | 121 | {`Product 122 |
123 | 124 | Package deal 125 | 126 | 127 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat, quasi? Pariatur ipsam voluptatum, 128 | quas labore amet dolor dolore, aspernatur tempora quasi ullam ad, autem distinctio doloribus! Iusto 129 | rem iste accusamus. 130 | 131 |
132 | ))} 133 |
134 |
135 |
136 |
137 | ); 138 | } 139 | -------------------------------------------------------------------------------- /src/templates/ecommerce/Home.tsx: -------------------------------------------------------------------------------- 1 | import Header from '../shared/components/Header'; 2 | import Content from './Content'; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 |
8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/product1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/ecommerce/assets/product1.png -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/product2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/ecommerce/assets/product2.png -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/product3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/ecommerce/assets/product3.png -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/product4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/ecommerce/assets/product4.png -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/product5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/ecommerce/assets/product5.png -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/product6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/ecommerce/assets/product6.png -------------------------------------------------------------------------------- /src/templates/ecommerce/assets/products.json: -------------------------------------------------------------------------------- 1 | { 2 | "listProducts": [ 3 | { 4 | "id": 1, 5 | "name": "Product 1", 6 | "price": 999.99, 7 | "image": "./assets/ecommerce/product1.png", 8 | "desc1": "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Facere corrupti odit modi adipisci facilis repellendus ab, officiis soluta beatae quaerat iusto fuga sed tenetur dicta maiores aut est eaque libero?", 9 | "desc2": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Nemo id suscipit beatae incidunt, fugiat ipsum nisi consequatur officiis, debitis deserunt aspernatur praesentium quo doloremque soluta quibusdam quam sunt laborum corrupti.", 10 | "defaultData": [ 11 | { 12 | "CPU": 1, 13 | "Memory": "2Gib", 14 | "Details": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Nemo id suscipit beatae incidunt." 15 | }, 16 | { 17 | "CPU": 2, 18 | "Memory": "4Gib", 19 | "Details": "Officiis eos illo vitae accusamus! Doloremque sapiente ducimus ullam. Assumenda ad sapiente deleniti!" 20 | }, 21 | { 22 | "CPU": 4, 23 | "Memory": "16Gib", 24 | "Details": "Provident ipsum earum, officiis obcaecati labore quisquam cum officia delectus minus dolor. Necessitatibus nemo illo pariatur velit voluptatibus cupiditate quas?" 25 | } 26 | ] 27 | }, 28 | { 29 | "id": 2, 30 | "name": "Product 2", 31 | "price": 99.99, 32 | "image": "./assets/img/ecommerce/product2.png", 33 | "desc1": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati." 34 | }, 35 | { 36 | "id": 3, 37 | "name": "Product 3", 38 | "price": 9.99, 39 | "image": "./assets/ecommerce/product3.png", 40 | "desc1": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati." 41 | }, 42 | { 43 | "id": 4, 44 | "name": "Product 4", 45 | "price": 59.99, 46 | "image": "./assets/ecommerce/product4.png", 47 | "desc1": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati." 48 | }, 49 | { 50 | "id": 1, 51 | "name": "Product 1", 52 | "price": 999.99, 53 | "image": "./assets/product1.png" 54 | }, 55 | { 56 | "id": 1, 57 | "name": "Product 1", 58 | "price": 999.99, 59 | "image": "./assets/product1.png" 60 | } 61 | ] 62 | } -------------------------------------------------------------------------------- /src/templates/foundation/Content.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Button, Label, Typography } from '@neo4j-ndl/react'; 3 | 4 | import { setDriver, disconnect } from '../shared/utils/Driver'; 5 | import ConnectionModal from '../shared/components/ConnectionModal'; 6 | 7 | export default function Content() { 8 | const [init, setInit] = useState(false); 9 | const [openConnection, setOpenConnection] = useState(false); 10 | const [connectionStatus, setConnectionStatus] = useState(false); 11 | 12 | useEffect(() => { 13 | if (!init) { 14 | let session = localStorage.getItem('needleStarterKit-neo4j.connection'); 15 | if (session) { 16 | let neo4jConnection = JSON.parse(session); 17 | setDriver(neo4jConnection.uri, neo4jConnection.user, neo4jConnection.password).then((isSuccessful: boolean) => { 18 | setConnectionStatus(isSuccessful); 19 | }); 20 | } 21 | setInit(true); 22 | } 23 | }); 24 | 25 | return ( 26 |
27 | 32 |
Your content goes here.
33 |
Happy coding!
34 | 35 | 36 | Neo4j connection Status: 37 | 38 | {!connectionStatus ? : } 39 | 40 | 41 | 42 | {!connectionStatus ? ( 43 | 44 | ) : ( 45 | 46 | )} 47 |
48 | ); 49 | } 50 | -------------------------------------------------------------------------------- /src/templates/foundation/Home.tsx: -------------------------------------------------------------------------------- 1 | import Header from '../shared/components/Header'; 2 | import PageLayout from './Layout/PageLayout'; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 |
8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/templates/foundation/Layout/PageLayout.tsx: -------------------------------------------------------------------------------- 1 | import SideNav from './SideNav'; 2 | import Content from '../Content'; 3 | 4 | export default function PageLayout() { 5 | return ( 6 |
7 | 8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/templates/foundation/Layout/SideNav.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { SideNavigation } from '@neo4j-ndl/react'; 3 | import { MagnifyingGlassIconOutline, DbmsIcon, BellAlertIconOutline } from '@neo4j-ndl/react/icons'; 4 | 5 | export default function SideNav() { 6 | const [expanded, setOnExpanded] = useState(!(window.innerWidth < 450)); 7 | const [selected, setSelected] = useState('instances'); 8 | const [isMobile] = useState(window.innerWidth < 450); 9 | 10 | const handleClick = (item: string) => (e: any) => { 11 | e.preventDefault(); 12 | setSelected(item); 13 | }; 14 | const fullSizeClasses = 'n-w-full n-h-full'; 15 | const expandedChangeProp = isMobile ? {} : { onExpandedChange: setOnExpanded }; 16 | 17 | return ( 18 |
19 | 20 | 21 | } : {})} 26 | icon={} 27 | > 28 | Search 29 | 30 | } 35 | > 36 | Instances 37 | 38 | Example 39 | } 44 | > 45 | Notifications 46 | 47 | 48 | 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/templates/movie/Content.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Typography, Tag, LoadingSpinner } from '@neo4j-ndl/react'; 2 | import { ChevronLeftIconOutline, ChevronRightIconOutline } from '@neo4j-ndl/react/icons'; 3 | 4 | import { MovieInterface } from './Interfaces'; 5 | import Movie from './Movie'; 6 | 7 | export default function Content({ 8 | loadingStates, 9 | mainMovie, 10 | recoSimilarGenre, 11 | recoOtherUsers, 12 | }: { 13 | loadingStates: { loadingMain: boolean; loadingSimilarGenre: boolean; loadingOtherUsers: boolean }; 14 | mainMovie: MovieInterface[]; 15 | recoSimilarGenre: MovieInterface[]; 16 | recoOtherUsers: MovieInterface[]; 17 | }) { 18 | return ( 19 |
20 |
21 | {/* Featured Movie */} 22 | {loadingStates.loadingMain ? ( 23 |
24 | 25 |
26 | ) : ( 27 |
28 | Product 1 29 |
30 | {mainMovie[0].title} 31 |
32 | {mainMovie[0].plot} 33 |
34 |
35 | {mainMovie[0].languages.map((lang: string, index: number) => ( 36 | 37 | {lang} 38 | 39 | ))} 40 |
41 |
42 | {mainMovie[0].genres.map((genre: string, index: number) => ( 43 | 44 | {genre} 45 | 46 | ))} 47 |
48 |
49 | Year: {mainMovie[0].year.toString()} 50 | IMDB Rating: {mainMovie[0].imdbRating} 51 |
52 |
53 |
54 | 55 | 56 |
57 |
58 |
59 |
60 | )} 61 | 62 | {/* Similar Genre */} 63 | {loadingStates.loadingSimilarGenre ? ( 64 |
65 | 66 |
67 | ) : ( 68 |
69 | Similar genre 70 |
71 | 72 |
73 | {recoSimilarGenre.map((movie, index) => ( 74 | 75 | ))} 76 |
77 | 78 |
79 |
80 | )} 81 | 82 | {/* User who watched this, also watched this */} 83 | {loadingStates.loadingOtherUsers ? ( 84 |
85 | 86 |
87 | ) : ( 88 |
89 | Users who watched {mainMovie[0]?.title} also watched this 90 |
91 | 92 |
93 | {recoOtherUsers.map((movie, index) => ( 94 | 95 | ))} 96 |
97 | 98 |
99 |
100 | )} 101 |
102 |
103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /src/templates/movie/Home.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Button, Flex, Typography } from '@neo4j-ndl/react'; 3 | 4 | import Header from '../shared/components/Header'; 5 | import { setDriver, runRecoQuery } from '../shared/utils/Driver'; 6 | import ConnectionModal from '../shared/components/ConnectionModal'; 7 | 8 | import Content from './Content'; 9 | import { MovieInterface } from './Interfaces'; 10 | import moviesData from './assets/movies.json'; 11 | import NoDataImg from '../shared/assets/NoData.png'; 12 | 13 | const mainMovieId = '79132'; 14 | const queryMainMovie = `MATCH (m:Movie {movieId: '${mainMovieId}'})-[:IN_GENRE]->(g:Genre) RETURN ID(m) as id, collect(g.name) as genres, m.year as year, m.imdbRating as imdbRating, m.languages as languages, m.title as title, m.plot as plot, m.poster as poster;`; 15 | const queryOtherUsersAlsoWatched = `MATCH (m:Movie {movieId: '${mainMovieId}'})<-[:RATED]-(u:User)-[:RATED]->(rec:Movie)-[:IN_GENRE]->(g:Genre) WITH g, rec, COUNT(*) AS usersWhoAlsoWatched ORDER BY usersWhoAlsoWatched DESC LIMIT 25 RETURN ID(rec) as id, collect(g.name) as genres, rec.year as year, rec.imdbRating as imdbRating, rec.languages as languages, rec.title as title, rec.plot as plot, rec.poster as poster LIMIT 6;`; 16 | const similarByGenre = `MATCH (m:Movie {movieId: '${mainMovieId}'})-[:IN_GENRE]->(g:Genre)<-[:IN_GENRE]-(rec:Movie) WITH rec, collect(g.name) AS genres, count(*) AS commonGenres RETURN ID(rec) as id, genres, rec.year as year, rec.imdbRating as imdbRating, rec.languages as languages, rec.title as title, rec.plot as plot, rec.poster as poster ORDER BY commonGenres DESC LIMIT 6;`; 17 | 18 | export default function Home() { 19 | const [useReco, setUseReco] = useState(false); 20 | const [recoError, setRecoError] = useState(false); 21 | const [isConnectionModalOpen, setIsConnectionModalOpen] = useState(false); 22 | 23 | const [loading, setLoading] = useState({ main: true, similarGenre: true, otherUsers: true }); 24 | 25 | const [mainMovie, setMainMovie] = useState([]); 26 | const [recoOtherUsers, setRecoOtherUsers] = useState([]); 27 | const [recoSimilarGenre, setRecoSimilarGenre] = useState([]); 28 | 29 | useEffect(() => { 30 | const fetchData = async () => { 31 | if (!useReco) { 32 | setRecoError(false); 33 | setLoading({ main: true, similarGenre: true, otherUsers: true }); 34 | setMainMovie([moviesData.listMovies[0]]); 35 | setRecoSimilarGenre(moviesData.listMovies.slice(1, 7)); 36 | setRecoOtherUsers(moviesData.listMovies.slice(7, 13)); 37 | setLoading({ main: false, similarGenre: false, otherUsers: false }); 38 | } else { 39 | setLoading({ main: true, similarGenre: true, otherUsers: true }); 40 | const { uri, user, password } = 41 | JSON.parse(localStorage.getItem('needleStarterKit-neo4j.connection') ?? '') ?? {}; 42 | setDriver(uri, user, password); 43 | await Promise.all([ 44 | fetchMovies(queryMainMovie).then((movies) => setMainMovie(movies || [])), 45 | fetchMovies(similarByGenre).then((movies) => setRecoSimilarGenre(movies || [])), 46 | fetchMovies(queryOtherUsersAlsoWatched).then((movies) => setRecoOtherUsers(movies || [])), 47 | ]); 48 | setLoading({ main: false, similarGenre: false, otherUsers: false }); 49 | } 50 | }; 51 | 52 | fetchData(); 53 | }, [useReco]); 54 | 55 | const fetchMovies = async (query: string) => { 56 | const result = await runRecoQuery(query); 57 | if (Array.isArray(result)) { 58 | if (result.length < 1) { 59 | setRecoError(true); 60 | } else { 61 | return result.map((movie) => ({ 62 | id: movie.id, 63 | genres: movie.genres, 64 | year: movie.year, 65 | imdbRating: movie.imdbRating, 66 | languages: movie.languages, 67 | title: movie.title, 68 | plot: movie.plot, 69 | poster: movie.poster, 70 | })); 71 | } 72 | } 73 | }; 74 | 75 | return ( 76 |
77 |
setIsConnectionModalOpen(true)} 83 | userHeader={false} 84 | /> 85 | {recoError ? ( 86 | 90 | 91 | 92 | Data error 93 | 94 |

An error occurred while fetching recommendations data.

95 |

96 | Please make sure you are connected to a Neo4j Database with the same data model as the{' '} 97 | 98 | 99 | Recommendation demo available here 100 | 101 | 102 |

103 |

104 | Alternatively, you can also use the one from{' '} 105 | 106 | 107 | Neo4j Sandbox 108 | 109 | 110 |

111 |
112 | 113 |
114 |
115 | ) : ( 116 | 126 | )} 127 | 137 | 139 | ); 140 | } 141 | -------------------------------------------------------------------------------- /src/templates/movie/Interfaces.tsx: -------------------------------------------------------------------------------- 1 | export interface MovieInterface { 2 | id: number; 3 | genres: string[]; 4 | languages: string[]; 5 | title: string; 6 | plot: string; 7 | poster: string; 8 | year: string; 9 | imdbRating: string; 10 | } 11 | -------------------------------------------------------------------------------- /src/templates/movie/Movie.tsx: -------------------------------------------------------------------------------- 1 | import { useState, CSSProperties } from 'react'; 2 | import { Typography, Box } from '@neo4j-ndl/react'; 3 | import { MovieInterface } from './Interfaces'; 4 | 5 | export default function Movie({ movie }: { movie: MovieInterface }) { 6 | const [isHovered, setIsHovered] = useState(false); 7 | 8 | const boxStyle: CSSProperties = { 9 | transition: 'all 0.3s ease', 10 | maxWidth: '400px', 11 | position: 'relative', 12 | transform: isHovered ? 'scale(1.5)' : 'scale(1)', 13 | zIndex: isHovered ? '10' : '1', 14 | boxShadow: isHovered ? '0px 0px 20px rgba(0,0,0,0.5)' : 'none', 15 | }; 16 | const infoStyle: CSSProperties = { 17 | position: 'absolute', 18 | bottom: 0, 19 | left: 0, 20 | right: 0, 21 | transform: isHovered ? 'translateY(0)' : 'translateY(100%)', 22 | display: isHovered ? 'inline-block' : 'none', 23 | transition: 'transform 1s ease', 24 | backgroundColor: 'rgb(var(--theme-palette-neutral-bg-stronger))', 25 | color: 'white', 26 | padding: '10px', 27 | }; 28 | 29 | return ( 30 | setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} style={boxStyle}> 31 | {movie.title} 32 |
33 | 34 | {movie.title} 35 | 36 | 47 | {movie.plot} 48 | 49 |
50 |
51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/templates/movie/assets/movies.json: -------------------------------------------------------------------------------- 1 | { 2 | "listMovies": [ 3 | { 4 | "id": 1, 5 | "genres": ["Genre1", "Genre2", "Genre3"], 6 | "languages": ["English"], 7 | "title": "Movie1", 8 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati. Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 9 | "poster": "./assets/movie/movie1.png", 10 | "year": "2024", 11 | "imdbRating": "10" 12 | }, 13 | { 14 | "id": 2, 15 | "genres": ["Genre1", "Genre2", "Genre3"], 16 | "languages": ["English"], 17 | "title": "Movie2", 18 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 19 | "poster": "./assets/movie/movie2.png", 20 | "year": "2024", 21 | "imdbRating": "10" 22 | }, 23 | { 24 | "id": 3, 25 | "genres": ["Genre1", "Genre2", "Genre3"], 26 | "languages": ["English"], 27 | "title": "Movie3", 28 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 29 | "poster": "./assets/movie/movie3.png", 30 | "year": "2024", 31 | "imdbRating": "10" 32 | }, 33 | { 34 | "id": 4, 35 | "genres": ["Genre1", "Genre2", "Genre3"], 36 | "languages": ["English"], 37 | "title": "Movie4", 38 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 39 | "poster": "./assets/movie/movie4.png", 40 | "year": "2024", 41 | "imdbRating": "10" 42 | }, 43 | { 44 | "id": 5, 45 | "genres": ["Genre1", "Genre2", "Genre3"], 46 | "languages": ["English"], 47 | "title": "Movie5", 48 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 49 | "poster": "./assets/movie/movie5.png", 50 | "year": "2024", 51 | "imdbRating": "10" 52 | }, 53 | { 54 | "id": 6, 55 | "genres": ["Genre1", "Genre2", "Genre3"], 56 | "languages": ["English"], 57 | "title": "Movie6", 58 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 59 | "poster": "./assets/movie/movie6.png", 60 | "year": "2024", 61 | "imdbRating": "10" 62 | }, 63 | { 64 | "id": 7, 65 | "genres": ["Genre1", "Genre2", "Genre3"], 66 | "languages": ["English"], 67 | "title": "Movie7", 68 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 69 | "poster": "./assets/movie/movie7.png", 70 | "year": "2024", 71 | "imdbRating": "10" 72 | }, 73 | { 74 | "id": 8, 75 | "genres": ["Genre1", "Genre2", "Genre3"], 76 | "languages": ["English"], 77 | "title": "Movie8", 78 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 79 | "poster": "./assets/movie/movie8.png", 80 | "year": "2024", 81 | "imdbRating": "10" 82 | }, 83 | { 84 | "id": 9, 85 | "genres": ["Genre1", "Genre2", "Genre3"], 86 | "languages": ["English"], 87 | "title": "Movie9", 88 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 89 | "poster": "./assets/movie/movie9.png", 90 | "year": "2024", 91 | "imdbRating": "10" 92 | }, 93 | { 94 | "id": 10, 95 | "genres": ["Genre1", "Genre2", "Genre3"], 96 | "languages": ["English"], 97 | "title": "Movie10", 98 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 99 | "poster": "./assets/movie/movie10.png", 100 | "year": "2024", 101 | "imdbRating": "10" 102 | }, 103 | { 104 | "id": 11, 105 | "genres": ["Genre1", "Genre2", "Genre3"], 106 | "languages": ["English"], 107 | "title": "Movie11", 108 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 109 | "poster": "./assets/movie/movie11.png", 110 | "year": "2024", 111 | "imdbRating": "10" 112 | }, 113 | { 114 | "id": 12, 115 | "genres": ["Genre1", "Genre2", "Genre3"], 116 | "languages": ["English"], 117 | "title": "Movie12", 118 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 119 | "poster": "./assets/movie/movie12.png", 120 | "year": "2024", 121 | "imdbRating": "10" 122 | }, 123 | { 124 | "id": 13, 125 | "genres": ["Genre1", "Genre2", "Genre3"], 126 | "languages": ["English"], 127 | "title": "Movie13", 128 | "plot": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque sunt eaque eveniet reprehenderit vero velit debitis facere beatae obcaecati.", 129 | "poster": "./assets/movie/movie13.png", 130 | "year": "2024", 131 | "imdbRating": "10" 132 | } 133 | ] 134 | } -------------------------------------------------------------------------------- /src/templates/shared/assets/ChatbotMessages.json: -------------------------------------------------------------------------------- 1 | { 2 | "listMessages": [ 3 | { 4 | "id": 1, 5 | "message": "Hi, I need help with creating a Cypher query for Neo4j.", 6 | "user": "user", 7 | "datetime": "01/01/2024 00:00:00" 8 | }, 9 | { 10 | "id": 2, 11 | "message": "Sure, I can help with that. What specific data are you looking to retrieve?", 12 | "user": "chatbot", 13 | "datetime": "01/01/2024 00:00:00" 14 | }, 15 | { 16 | "id": 3, 17 | "message": "I need to find all employees who work in the IT department.", 18 | "user": "user", 19 | "datetime": "01/01/2024 00:00:00" 20 | }, 21 | { 22 | "id": 4, 23 | "message": 24 | "Alright, you can use the following query: `MATCH (e:Employee)-[:WORKS_IN]->(d:Department {name: 'IT'}) RETURN e.name`. This query matches nodes labeled 'Employee' related to the 'IT' department and returns their names.", 25 | "user": "chatbot", 26 | "datetime": "01/01/2024 00:00:00" 27 | }, 28 | { 29 | "id": 5, 30 | "message": "Thanks! And how do I get the total number of such employees?", 31 | "user": "user", 32 | "datetime": "01/01/2024 00:00:00" 33 | }, 34 | { 35 | "id": 6, 36 | "message": 37 | "To get the count, use: `MATCH (e:Employee)-[:WORKS_IN]->(d:Department {name: 'IT'}) RETURN count(e)`. This counts all the distinct 'Employee' nodes related to the 'IT' department.", 38 | "user": "chatbot", 39 | "datetime": "01/01/2024 00:00:00" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /src/templates/shared/assets/NoData.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/shared/assets/NoData.png -------------------------------------------------------------------------------- /src/templates/shared/assets/NotFound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/shared/assets/NotFound.png -------------------------------------------------------------------------------- /src/templates/shared/assets/chatbot-ai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/shared/assets/chatbot-ai.png -------------------------------------------------------------------------------- /src/templates/shared/assets/chatbot-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-labs/neo4j-needle-starterkit/3090135dd0503460d6142d5f7a7296d4d3df25a1/src/templates/shared/assets/chatbot-user.png -------------------------------------------------------------------------------- /src/templates/shared/components/Chatbot.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-confusing-arrow */ 2 | import { useEffect, useRef, useState } from 'react'; 3 | import { Button, Widget, Typography, Avatar, TextInput } from '@neo4j-ndl/react'; 4 | 5 | import ChatBotUserAvatar from '../assets/chatbot-user.png'; 6 | import ChatBotAvatar from '../assets/chatbot-ai.png'; 7 | 8 | type ChatbotProps = { 9 | messages: { 10 | id: number; 11 | user: string; 12 | message: string; 13 | datetime: string; 14 | isTyping?: boolean; 15 | }[]; 16 | }; 17 | 18 | export default function Chatbot(props: ChatbotProps) { 19 | const { messages } = props; 20 | const [listMessages, setListMessages] = useState(messages); 21 | const [inputMessage, setInputMessage] = useState(''); 22 | const formattedTextStyle = { color: 'rgb(var(--theme-palette-discovery-bg-strong))' }; 23 | 24 | const messagesEndRef = useRef(null); 25 | 26 | const handleInputChange = (e: React.ChangeEvent) => { 27 | setInputMessage(e.target.value); 28 | }; 29 | 30 | const simulateTypingEffect = (responseText: string, index = 0) => { 31 | if (index < responseText.length) { 32 | const nextIndex = index + 1; 33 | const currentTypedText = responseText.substring(0, nextIndex); 34 | 35 | if (index === 0) { 36 | const date = new Date(); 37 | const datetime = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; 38 | setListMessages((msgs) => [ 39 | ...msgs, 40 | { id: Date.now(), user: 'chatbot', message: currentTypedText, datetime: datetime, isTyping: true }, 41 | ]); 42 | } else { 43 | setListMessages((msgs) => msgs.map((msg) => (msg.isTyping ? { ...msg, message: currentTypedText } : msg))); 44 | } 45 | 46 | setTimeout(() => simulateTypingEffect(responseText, nextIndex), 20); 47 | } else { 48 | setListMessages((msgs) => msgs.map((msg) => (msg.isTyping ? { ...msg, isTyping: false } : msg))); 49 | } 50 | }; 51 | 52 | const handleSubmit = (e: { preventDefault: () => void }) => { 53 | e.preventDefault(); 54 | if (!inputMessage.trim()) { 55 | return; 56 | } 57 | const date = new Date(); 58 | const datetime = `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; 59 | const userMessage = { id: 999, user: 'user', message: inputMessage, datetime: datetime }; 60 | setListMessages((listMessages) => [...listMessages, userMessage]); 61 | setInputMessage(''); 62 | 63 | const chatbotReply = 'Hello Sir, how can I help you today?'; // Replace with getting a response from your chatbot through your APIs 64 | simulateTypingEffect(chatbotReply); 65 | }; 66 | 67 | const scrollToBottom = () => { 68 | messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); 69 | }; 70 | 71 | useEffect(() => { 72 | scrollToBottom(); 73 | }, [listMessages]); 74 | 75 | return ( 76 |
77 |
78 | 79 |
80 | {listMessages.map((chat) => ( 81 |
86 |
87 | {chat.user === 'chatbot' ? ( 88 | 98 | ) : ( 99 | 109 | )} 110 |
111 | 118 |
119 | {chat.message.split(/`(.+?)`/).map((part, index) => 120 | index % 2 === 1 ? ( 121 | 122 | {part} 123 | 124 | ) : ( 125 | part 126 | ) 127 | )} 128 |
129 |
130 | {chat.datetime} 131 |
132 |
133 |
134 | ))} 135 |
136 |
137 |
138 |
139 |
140 | 147 | 148 | 149 |
150 |
151 | ); 152 | } 153 | -------------------------------------------------------------------------------- /src/templates/shared/components/ConnectionModal.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Dialog, TextInput, Dropdown, Banner, Dropzone } from '@neo4j-ndl/react'; 2 | import { useState } from 'react'; 3 | import { setDriver } from '../utils/Driver'; 4 | 5 | interface Message { 6 | type: 'success' | 'info' | 'warning' | 'danger' | 'neutral'; 7 | content: string; 8 | } 9 | 10 | interface ConnectionModalProps { 11 | open: boolean; 12 | setOpenConnection: (arg: boolean) => void; 13 | setConnectionStatus: (status: boolean) => void; 14 | message?: Message; 15 | } 16 | 17 | export default function ConnectionModal({ 18 | open, 19 | setOpenConnection, 20 | setConnectionStatus, 21 | message, 22 | }: ConnectionModalProps) { 23 | const protocols = ['neo4j', 'neo4j+s', 'neo4j+ssc', 'bolt', 'bolt+s', 'bolt+ssc']; 24 | const [protocol, setProtocol] = useState('neo4j+s'); 25 | const [URI, setURI] = useState('localhost'); 26 | const [port, setPort] = useState(7687); 27 | const [database, setDatabase] = useState('neo4j'); 28 | const [username, setUsername] = useState('neo4j'); 29 | const [password, setPassword] = useState('password'); 30 | const [connectionMessage, setMessage] = useState(null); 31 | 32 | const [isLoading, setIsLoading] = useState(false); 33 | 34 | const parseAndSetURI = (uri: string) => { 35 | const uriParts = uri.split('://'); 36 | const uriHost = uriParts.pop() || URI; 37 | setURI(uriHost); 38 | const uriProtocol = uriParts.pop() || protocol; 39 | setProtocol(uriProtocol); 40 | const uriPort = Number(uriParts.pop()) || port; 41 | setPort(uriPort); 42 | }; 43 | 44 | const handleHostPasteChange: React.ClipboardEventHandler = (event) => { 45 | event.clipboardData.items[0]?.getAsString((value) => { 46 | parseAndSetURI(value); 47 | }); 48 | }; 49 | 50 | const onDropHandler = async (files: Partial[]) => { 51 | setIsLoading(true); 52 | if (files.length) { 53 | const [file] = files; 54 | 55 | if (file.text) { 56 | const text = await file.text(); 57 | const lines = text.split(/\r?\n/); 58 | const configObject = lines.reduce((acc: Record, line: string) => { 59 | if (line.startsWith('#') || line.trim() === '') { 60 | return acc; 61 | } 62 | 63 | const [key, value] = line.split('='); 64 | if (['NEO4J_URI', 'NEO4J_USERNAME', 'NEO4J_PASSWORD', 'NEO4J_DATABASE'].includes(key)) { 65 | acc[key] = value; 66 | } 67 | return acc; 68 | }, {}); 69 | parseAndSetURI(configObject.NEO4J_URI); 70 | setUsername(configObject.NEO4J_USERNAME); 71 | setPassword(configObject.NEO4J_PASSWORD); 72 | setDatabase(configObject.NEO4J_DATABASE); 73 | } 74 | } 75 | setIsLoading(false); 76 | }; 77 | 78 | function submitConnection() { 79 | const connectionURI = `${protocol}://${URI}${URI.split(':')[1] ? '' : `:${port}`}`; 80 | setDriver(connectionURI, username, password).then((isSuccessful) => { 81 | setConnectionStatus(isSuccessful); 82 | isSuccessful 83 | ? setOpenConnection(false) 84 | : setMessage({ 85 | type: 'danger', 86 | content: 'Connection failed, please check the developer console logs for more informations', 87 | }); 88 | }); 89 | } 90 | 91 | return ( 92 | <> 93 | 94 | Connect to Neo4j 95 | 96 | {message && {message.content}} 97 | {connectionMessage && {connectionMessage.content}} 98 |
99 | Drop your env file here} 102 | className='n-p-6 end-0 top-0 w-full h-full' 103 | acceptedFileExtensions={['.txt', '.env']} 104 | dropZoneOptions={{ 105 | onDrop: (f: Partial[]) => { 106 | onDropHandler(f); 107 | }, 108 | maxSize: 500, 109 | onDropRejected: (e) => { 110 | if (e.length) { 111 | // eslint-disable-next-line no-console 112 | console.log(`Failed To Upload, File is larger than 500 bytes`); 113 | } 114 | }, 115 | }} 116 | /> 117 | {isLoading &&
Loading...
} 118 |
119 |
120 | newValue && setProtocol(newValue.value), 128 | options: protocols.map((option) => ({ label: option, value: option })), 129 | value: { label: protocol, value: protocol }, 130 | }} 131 | className='w-1/4 inline-block' 132 | fluid 133 | /> 134 |
135 | setURI(e.target.value)} 144 | onPaste={(e) => handleHostPasteChange(e)} 145 | /> 146 |
147 |
148 | setDatabase(e.target.value)} 156 | className='w-full' 157 | /> 158 |
159 |
160 | setUsername(e.target.value)} 168 | /> 169 |
170 |
171 | setPassword(e.target.value)} 180 | /> 181 |
182 |
183 | 184 |
185 |
186 | 187 | ); 188 | } 189 | -------------------------------------------------------------------------------- /src/templates/shared/components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { MoonIconOutline, SunIconOutline, QuestionMarkCircleIconOutline } from '@neo4j-ndl/react/icons'; 2 | import { Typography, IconButton, Tabs, Switch, Logo } from '@neo4j-ndl/react'; 3 | import React, { useState } from 'react'; 4 | import { ThemeWrapperContext } from '../../../context/ThemeWrapper'; 5 | import User from './User'; 6 | 7 | export default function Header({ 8 | title, 9 | navItems = [], 10 | activeNavItem = navItems[0], 11 | setActiveNavItem = () => {}, 12 | useNeo4jConnect = false, 13 | connectNeo4j = false, 14 | setConnectNeo4j = () => {}, 15 | openConnectionModal = () => {}, 16 | userHeader = true, 17 | }: { 18 | title: string; 19 | navItems?: string[]; 20 | activeNavItem?: string; 21 | setActiveNavItem?: (activeNavItem: string) => void; 22 | useNeo4jConnect?: boolean; 23 | connectNeo4j?: boolean; 24 | setConnectNeo4j?: (connectNeo4j: boolean) => void; 25 | openConnectionModal?: () => void; 26 | userHeader?: boolean; 27 | }) { 28 | const themeUtils = React.useContext(ThemeWrapperContext); 29 | const [themeMode, setThemeMode] = useState(themeUtils.colorMode); 30 | 31 | const toggleColorMode = () => { 32 | setThemeMode((prevThemeMode) => { 33 | return prevThemeMode === 'light' ? 'dark' : 'light'; 34 | }); 35 | themeUtils.toggleColorMode(); 36 | }; 37 | 38 | return ( 39 |
40 | 113 |
114 | ); 115 | } 116 | -------------------------------------------------------------------------------- /src/templates/shared/components/PageNotFound.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Flex, Typography } from '@neo4j-ndl/react'; 2 | import { useNavigate } from 'react-router-dom'; 3 | 4 | import NotFoundImg from '../assets/NotFound.png'; 5 | 6 | export default function PageNotFound() { 7 | const navigate = useNavigate(); 8 | 9 | return ( 10 |
11 | 12 | 13 | 404 - Page Not Found 14 | 15 |

It looks like the page you are trying to access either does not exists or is currently unavailable

16 |

Please try again later and if the problem persists contact us

17 |
18 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/templates/shared/components/User.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Menu, Typography, IconButton, Avatar } from '@neo4j-ndl/react'; 3 | import { ChevronDownIconOutline } from '@neo4j-ndl/react/icons'; 4 | 5 | const settings = ['Profile', 'Logout']; 6 | 7 | export default function User() { 8 | const [anchorEl, setAnchorEl] = useState(null); 9 | const handleClick = (event: React.MouseEvent | React.KeyboardEvent) => { 10 | setAnchorEl(event.currentTarget); 11 | }; 12 | const handleClose = () => { 13 | setAnchorEl(null); 14 | }; 15 | 16 | const menuSelect = (e: string) => { 17 | window.alert(e); 18 | handleClose(); 19 | }; 20 | 21 | const open = Boolean(anchorEl); 22 | 23 | return ( 24 |
29 | 30 | 31 |
32 | 33 | John Doe 34 | 35 | 36 | 37 | john.doe@neo4j.com 38 | 39 | 40 | 41 | 42 | {settings.map((setting) => ( 43 | menuSelect(setting)} title={setting} /> 44 | ))} 45 | 46 | 47 |
48 | 49 | 50 | 51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/templates/shared/utils/Driver.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import neo4j, { Driver } from 'neo4j-driver'; 3 | 4 | export let driver: Driver; 5 | 6 | export async function setDriver(connectionURI: string, username: string, password: string) { 7 | try { 8 | driver = neo4j.driver(connectionURI, neo4j.auth.basic(username, password)); 9 | await driver.getServerInfo(); 10 | localStorage.setItem( 11 | 'needleStarterKit-neo4j.connection', 12 | JSON.stringify({ uri: connectionURI, user: username, password: password }) 13 | ); 14 | return true; 15 | } catch (err) { 16 | console.error(`Connection error\n${err}\nCause: ${err as Error}`); 17 | return false; 18 | } 19 | } 20 | 21 | export async function disconnect() { 22 | try { 23 | await driver.close(); 24 | return true; 25 | } catch (err) { 26 | console.error(`Disconnection error\n${err}\nCause: ${err as Error}`); 27 | return false; 28 | } 29 | } 30 | 31 | /* 32 | Everything below this line is only for providing examples based on datasets available in Neo4j Sandbox (sandbox.neo4j.com). 33 | When using this code in your own project, you should remove the examples below and use your own queries. 34 | */ 35 | export async function runRecoQuery(query: string) { 36 | const reco = []; 37 | try { 38 | let { records } = await driver.executeQuery(query); 39 | for (let record of records) { 40 | reco.push({ 41 | id: record.get('id'), 42 | genres: record.get('genres'), 43 | year: record.get('year'), 44 | imdbRating: record.get('imdbRating'), 45 | languages: record.get('languages'), 46 | title: record.get('title'), 47 | plot: record.get('plot'), 48 | poster: record.get('poster'), 49 | }); 50 | } 51 | 52 | return reco; 53 | } catch (err) { 54 | console.error(`Disconnection error\n${err}\nCause: ${err as Error}`); 55 | return false; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | interface ImportMetaEnv { 3 | readonly PACKAGE_VERSION: string; 4 | } 5 | 6 | interface ImportMeta { 7 | readonly env: ImportMetaEnv; 8 | } 9 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | import { tailwindConfig } from '@neo4j-ndl/base'; 2 | /** @type {import('tailwindcss').Config} */ 3 | export default { 4 | content: [ 5 | './public/index.html', 6 | './src/**/*.{html,tsx}', 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } 13 | 14 | module.exports = { 15 | presets: [tailwindConfig], 16 | prefix: '', 17 | corePlugins: { 18 | preflight: false, 19 | }, 20 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 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": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "resolveJsonModule": true 9 | }, 10 | "include": ["vite.config.ts", "./package.json"] 11 | } 12 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import packageJson from './package.json'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | define: { 9 | 'import.meta.env.PACKAGE_VERSION': JSON.stringify(packageJson.version), 10 | }, 11 | server: { 12 | port: 3000, 13 | }, 14 | }); 15 | --------------------------------------------------------------------------------