CREATE TABLE IF NOT EXISTS person (
+ id INTEGER PRIMARY KEY NOT NULL,
+ name VARCHAR(250) NOT NULL
+);
+diff --git a/apps/valor-software-site/src/assets/articles/articlesList.ts b/apps/valor-software-site/src/assets/articles/articlesList.ts index cdc9acb55..cc848e914 100644 --- a/apps/valor-software-site/src/assets/articles/articlesList.ts +++ b/apps/valor-software-site/src/assets/articles/articlesList.ts @@ -1,3 +1,3 @@ -export const articlesRefactoringTitlesList = ["0084-painless-cli-integration-testing", "0083-google-translate-customization-2-under-nextjs", "0082-google-translate-customization-under-nextjs", "0081-exploring-asynchronous-programming-approaches-in-python-mastering-asynchronous-programming-in-python", "0080-the-interview-under-the-hood", "0079-game-n-qwik-the-final-episode", "0078-game-n-qwik-episode-03", "0077-game-n-qwik-episode-02", "0076-game-n-qwik-episode-01", "0075-fostering-innovation-through-collaboration-outside-contributors-first-hand-experience", "0074-developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way", "0073-qwik-reaches-the-v1-so-does-qwik-nx", "0072-the-evolution-of-web-application-development", "0071-zero-cost-way-on-react-d3", "0070-multi-threading-for-impatient-rust-learners", "0069-module-federation-v7-featuring-delegate-modules-part-2", "0068-node-rust-friendship-forever-the-napi-rs-way", "0067-module-federation-v7-featuring-delegate-modules", "0066-design-processes-automation-with-ai-services", "0065-react-native-and-codepush", "0064-monitoring-the-progress-of-an-http-request-in-nestjs-via-websockets", "0063-leveraging-angular-15-host-directives", "0062-module-federation-with-ssr-and-react-18", "0061-tackling-breaking-changes-using-nx-workspace-generators", "0060-medusa-the-new-year-dedicated-release", "0059-avoiding-common-pitfalls-with-controlvalueaccessors-in-angular", "0058-migrating-a-repo-to-an-nx-monorepo-while-retaining-history", "0057-valor-software-medusa-app-and-github", "0056-a-qwik-view-of-the-ranking-bar", "0055-rendering-nativescript-angular-templates-and-components-into-images", "0054-medusa-resources-overlimit-in-realtime-notification", "0053-modernization-with-module-federation-nx-and-react", "0052-module-federation-for-the-business", "0051-tasty-recipes-for-react-d3-the-ranking-bar", "0050-introduction-to-jetpack-compose-for-nativescript", "0049-module-federation-in-mobile-apps-powered-by-nativescript", "0048-webpack-plugin-written-by-chatgpt", "0047-introduction-to-swiftui-for-nativescript", "0046-change-is-the-only-way-to-stay-your-true-self", "0045-turbopack-new-hotness-or-promising-alpha", "0044-medusa-the-only-commercial-available-saas-platform-for-federated-applications", "0043-orchestrate-your-frontend-components-with-visual-dependency-graphs", "0042-next-js-module-federation-and-module-federation-ssr-plugins-are-being-open-sourced", "0041-nx-next-js-e-module-federation", "0040-nx-next-js-and-module-federation", "0039-debugging-ngrx-in-nativescript-with-redux-devtools", "0038-announcing-strategic-partnership-with-zack-jackson-the-module-federation-inventor", "0037-implementing-websockets-plugin-for-nativescript-using-react-native", "0036-tagtide-library-make-your-html-editor-friendly-and-more", "0035-diving-into-seeking-issue-with-mp3-files", "0034-performance-testing-via-artillery-io", "0033-arc-a-new-weapon-against-accessibility-bugs", "0032-setting-up-your-project-on-gcp-fast-using-terraform-and-kubernetes", "0031-ngx-bootstrap-v7-0-0-is-officially-released", "0030-how-to-deploy-firebase-preview-channels-on-travis-ci", "0029-scully-helped-us-reach-a-99-lighthouse-score-for-a-b2c-platform", "0028-designing-aggregator-app-from-a-to-z-part-2", "0027-designing-aggregator-app-from-a-to-z-part-1", "0026-multi-highlighting-for-draftjs", "0025-new-year-new-valor-new-you", "0024-incorporating-user-research-on-live-projects-part-4", "0023-incorporating-user-research-on-live-projects-part-3", "0022-incorporating-user-research-on-live-projects-part-2", "0021-incorporating-user-research-on-live-projects-part-1", "0020-why-you-might-want-to-switch-to-time-and-material-from-fixed-price", "0019-cross-site-scripting-xss-a-qa-engineers-guide", "0018-career-path-for-a-flat-structured-company", "0017-json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server", "0016-testing-ios-app-vulnerabilities-with-jailbreaking-part-1", "0015-benefits-of-agile-to-business-and-team", "0014-valor-software-named-top-development-company-in-ukraine-by-clutch", "0013-testing-with-protractor-how-to-fix-synchronization-issues", "0012-hacktoberfest-2019-is-coming-and-ngx-bootstrap-strives-for-your-attention", "0011-the-4-biggest-lessons-we-learned-while-building-a-startup-product-as-an-outsource-company", "0010-angular-and-seo-structured-data-for-rich-snippets", "0009-ngx-bootstrap-angular-ivy-is-here", "0008-webflow-custom-code-snippets-you-would-use-episode-1", "0007-testing-canvas-could-be-easier", "0006-case-study-of-applitools-or-not-only-cypress-cross-browser-testing", "0005-figma-as-a-presentation-tool-unifying-templates", "0004-cypress-testing-running-tests-in-parallel", "0003-quality-assured-what-it-really-takes-to-test-open-source-libraries", "0002-figma-vs-sketch-text-alignment-comparison", "0001-como-o-scully-nos-ajudou-a-atingir-99-pontos-no-lighthouse-para-uma-plataforma-b2c"]; -export const articlesList = ["Painless CLI integration testing", "Google Translate customization#2 under NextJS", "Google Translate customization under NextJS", "Exploring Asynchronous Programming Approaches in Python (Mastering Asynchronous Programming in Python)", "The Interview Under the Hood", "Game-n-Qwik. The Final Episode.", "Game-n-Qwik. Episode 03.", "Game-n-Qwik. Episode 02.", "Game-n-Qwik. Episode 01.", "Fostering Innovation Through Collaboration: Outside Contributor's First-Hand Experience", "Developing a Desktop Application via Rust and NextJS. The Tauri Way.", "Qwik reaches the v1, so does qwik-nx!", "The Evolution of Web Application Development", "Zero-cost Way on React & D3.", "Multi-threading for Impatient Rust Learners.", "Module Federation v7 featuring Delegate Modules Part 2", "Node & Rust: Friendship Forever. The NAPI-rs Way.", "Module Federation v7 featuring Delegate Modules", "Design processes automation with AI services", "React Native and CodePush", "Monitoring the Progress of an HTTP Request in NestJS via WebSockets", "Leveraging @angular 15 host directives", "Module Federation with SSR and React 18", "Tackling breaking changes using Nx Workspace generators", "Medusa the New Year-dedicated Release", "Avoiding common pitfalls with ControlValueAccessors in Angular", "Migrating a repo to an Nx monorepo while retaining history", "Valor Software Medusa app and GitHub", "A Qwik View of the Ranking Bar", "Rendering NativeScript Angular Templates and Components into Images", "Medusa Resources Overlimit in Realtime Notification", "Modernization with Module Federation, Nx and React", "Module Federation for the Business", "Tasty Recipes for React & D3. The Ranking Bar", "Introduction to Jetpack Compose for NativeScript", "Module Federation in mobile apps powered by NativeScript", "Webpack Plugin written by ChatGPT", "Introduction to SwiftUI for NativeScript", "Change is the only way to stay your true self", "Turbopack, new Hotness or promising Alpha", "Medusa – the Only Commercial Available SaaS Platform for Federated Applications", "Orchestrate your frontend components with visual dependency graphs!", "Next.js Module Federation and Module Federation SSR plugins are being open-sourced", "Nx, Next.js, e Module Federation", "Nx, Next.js, and Module Federation", "Debugging NgRx in NativeScript with Redux DevTools", "Announcing strategic partnership with Zack Jackson, the Module Federation inventor", "Implementing WebSockets plugin for NativeScript using React Native", "TagTide library: make your HTML editor-friendly and more", "Diving into seeking issue with MP3 files", "Performance Testing via Artillery.io", "ARC – a new weapon against accessibility bugs", "Setting up your project on GCP fast using Terraform and Kubernetes", "ngx-bootstrap v7.0.0 is officially released!", "How to deploy Firebase Preview Channels on Travis CI", "Scully Helped us Reach a 99 Lighthouse Score for a B2C Platform", "Designing Aggregator App from A to Z – Part 2", "Designing Aggregator App from A to Z — Part 1", "Multi-highlighting for DraftJS", "New year, new Valor, new you", "Incorporating user research on live projects — Part 4", "Incorporating user research on live projects — Part 3", "Incorporating user research on live projects — Part 2", "Incorporating user research on live projects — Part 1", "Why you might want to switch to Time and Material from Fixed Price", "Cross-Site Scripting (XSS): a QA Engineer's Guide", "Career Path for a Flat-structured company", "JSON Web Token Authorization with Access and Refresh Tokens in Angular Application with Node.js Server", "Testing iOS App Vulnerabilities with Jailbreaking. Part 1.", "Benefits of Agile to Business and Team", "Valor Software Named Top Development Company in Ukraine by Clutch", "Testing with Protractor: how to fix synchronization issues", "Hacktoberfest 2019 is coming, and ngx-bootstrap strives for your attention!", "The 4 biggest lessons we learned while building a startup product as an outsource company", "Angular and SEO: Structured Data for Rich Snippets", "ngx-bootstrap: Angular Ivy is here", "Webflow custom code snippets you would use. Episode 1", "Testing Canvas could be easier", "Case study of Applitools or not only Cypress cross-browser testing", "Figma as a presentation tool: Unifying templates", "Cypress testing: Running tests in parallel", "Quality Assured: What It Really Takes To Test Open Source Libraries", "Figma Vs Sketch: Text alignment comparison", "Como o Scully nos ajudou a atingir 99 pontos no Lighthouse para uma plataforma B2C"]; -const orderNumberForNextArticle = 85; \ No newline at end of file +export const articlesRefactoringTitlesList = ["0085-tauri-crud-boilerplate", "0084-painless-cli-integration-testing", "0083-google-translate-customization-2-under-nextjs", "0082-google-translate-customization-under-nextjs", "0081-exploring-asynchronous-programming-approaches-in-python-mastering-asynchronous-programming-in-python", "0080-the-interview-under-the-hood", "0079-game-n-qwik-the-final-episode", "0078-game-n-qwik-episode-03", "0077-game-n-qwik-episode-02", "0076-game-n-qwik-episode-01", "0075-fostering-innovation-through-collaboration-outside-contributors-first-hand-experience", "0074-developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way", "0073-qwik-reaches-the-v1-so-does-qwik-nx", "0072-the-evolution-of-web-application-development", "0071-zero-cost-way-on-react-d3", "0070-multi-threading-for-impatient-rust-learners", "0069-module-federation-v7-featuring-delegate-modules-part-2", "0068-node-rust-friendship-forever-the-napi-rs-way", "0067-module-federation-v7-featuring-delegate-modules", "0066-design-processes-automation-with-ai-services", "0065-react-native-and-codepush", "0064-monitoring-the-progress-of-an-http-request-in-nestjs-via-websockets", "0063-leveraging-angular-15-host-directives", "0062-module-federation-with-ssr-and-react-18", "0061-tackling-breaking-changes-using-nx-workspace-generators", "0060-medusa-the-new-year-dedicated-release", "0059-avoiding-common-pitfalls-with-controlvalueaccessors-in-angular", "0058-migrating-a-repo-to-an-nx-monorepo-while-retaining-history", "0057-valor-software-medusa-app-and-github", "0056-a-qwik-view-of-the-ranking-bar", "0055-rendering-nativescript-angular-templates-and-components-into-images", "0054-medusa-resources-overlimit-in-realtime-notification", "0053-modernization-with-module-federation-nx-and-react", "0052-module-federation-for-the-business", "0051-tasty-recipes-for-react-d3-the-ranking-bar", "0050-introduction-to-jetpack-compose-for-nativescript", "0049-module-federation-in-mobile-apps-powered-by-nativescript", "0048-webpack-plugin-written-by-chatgpt", "0047-introduction-to-swiftui-for-nativescript", "0046-change-is-the-only-way-to-stay-your-true-self", "0045-turbopack-new-hotness-or-promising-alpha", "0044-medusa-the-only-commercial-available-saas-platform-for-federated-applications", "0043-orchestrate-your-frontend-components-with-visual-dependency-graphs", "0042-next-js-module-federation-and-module-federation-ssr-plugins-are-being-open-sourced", "0041-nx-next-js-e-module-federation", "0040-nx-next-js-and-module-federation", "0039-debugging-ngrx-in-nativescript-with-redux-devtools", "0038-announcing-strategic-partnership-with-zack-jackson-the-module-federation-inventor", "0037-implementing-websockets-plugin-for-nativescript-using-react-native", "0036-tagtide-library-make-your-html-editor-friendly-and-more", "0035-diving-into-seeking-issue-with-mp3-files", "0034-performance-testing-via-artillery-io", "0033-arc-a-new-weapon-against-accessibility-bugs", "0032-setting-up-your-project-on-gcp-fast-using-terraform-and-kubernetes", "0031-ngx-bootstrap-v7-0-0-is-officially-released", "0030-how-to-deploy-firebase-preview-channels-on-travis-ci", "0029-scully-helped-us-reach-a-99-lighthouse-score-for-a-b2c-platform", "0028-designing-aggregator-app-from-a-to-z-part-2", "0027-designing-aggregator-app-from-a-to-z-part-1", "0026-multi-highlighting-for-draftjs", "0025-new-year-new-valor-new-you", "0024-incorporating-user-research-on-live-projects-part-4", "0023-incorporating-user-research-on-live-projects-part-3", "0022-incorporating-user-research-on-live-projects-part-2", "0021-incorporating-user-research-on-live-projects-part-1", "0020-why-you-might-want-to-switch-to-time-and-material-from-fixed-price", "0019-cross-site-scripting-xss-a-qa-engineers-guide", "0018-career-path-for-a-flat-structured-company", "0017-json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server", "0016-testing-ios-app-vulnerabilities-with-jailbreaking-part-1", "0015-benefits-of-agile-to-business-and-team", "0014-valor-software-named-top-development-company-in-ukraine-by-clutch", "0013-testing-with-protractor-how-to-fix-synchronization-issues", "0012-hacktoberfest-2019-is-coming-and-ngx-bootstrap-strives-for-your-attention", "0011-the-4-biggest-lessons-we-learned-while-building-a-startup-product-as-an-outsource-company", "0010-angular-and-seo-structured-data-for-rich-snippets", "0009-ngx-bootstrap-angular-ivy-is-here", "0008-webflow-custom-code-snippets-you-would-use-episode-1", "0007-testing-canvas-could-be-easier", "0006-case-study-of-applitools-or-not-only-cypress-cross-browser-testing", "0005-figma-as-a-presentation-tool-unifying-templates", "0004-cypress-testing-running-tests-in-parallel", "0003-quality-assured-what-it-really-takes-to-test-open-source-libraries", "0002-figma-vs-sketch-text-alignment-comparison", "0001-como-o-scully-nos-ajudou-a-atingir-99-pontos-no-lighthouse-para-uma-plataforma-b2c"]; +export const articlesList = ["Tauri CRUD Boilerplate", "Painless CLI integration testing", "Google Translate customization#2 under NextJS", "Google Translate customization under NextJS", "Exploring Asynchronous Programming Approaches in Python (Mastering Asynchronous Programming in Python)", "The Interview Under the Hood", "Game-n-Qwik. The Final Episode.", "Game-n-Qwik. Episode 03.", "Game-n-Qwik. Episode 02.", "Game-n-Qwik. Episode 01.", "Fostering Innovation Through Collaboration: Outside Contributor's First-Hand Experience", "Developing a Desktop Application via Rust and NextJS. The Tauri Way.", "Qwik reaches the v1, so does qwik-nx!", "The Evolution of Web Application Development", "Zero-cost Way on React & D3.", "Multi-threading for Impatient Rust Learners.", "Module Federation v7 featuring Delegate Modules Part 2", "Node & Rust: Friendship Forever. The NAPI-rs Way.", "Module Federation v7 featuring Delegate Modules", "Design processes automation with AI services", "React Native and CodePush", "Monitoring the Progress of an HTTP Request in NestJS via WebSockets", "Leveraging @angular 15 host directives", "Module Federation with SSR and React 18", "Tackling breaking changes using Nx Workspace generators", "Medusa the New Year-dedicated Release", "Avoiding common pitfalls with ControlValueAccessors in Angular", "Migrating a repo to an Nx monorepo while retaining history", "Valor Software Medusa app and GitHub", "A Qwik View of the Ranking Bar", "Rendering NativeScript Angular Templates and Components into Images", "Medusa Resources Overlimit in Realtime Notification", "Modernization with Module Federation, Nx and React", "Module Federation for the Business", "Tasty Recipes for React & D3. The Ranking Bar", "Introduction to Jetpack Compose for NativeScript", "Module Federation in mobile apps powered by NativeScript", "Webpack Plugin written by ChatGPT", "Introduction to SwiftUI for NativeScript", "Change is the only way to stay your true self", "Turbopack, new Hotness or promising Alpha", "Medusa – the Only Commercial Available SaaS Platform for Federated Applications", "Orchestrate your frontend components with visual dependency graphs!", "Next.js Module Federation and Module Federation SSR plugins are being open-sourced", "Nx, Next.js, e Module Federation", "Nx, Next.js, and Module Federation", "Debugging NgRx in NativeScript with Redux DevTools", "Announcing strategic partnership with Zack Jackson, the Module Federation inventor", "Implementing WebSockets plugin for NativeScript using React Native", "TagTide library: make your HTML editor-friendly and more", "Diving into seeking issue with MP3 files", "Performance Testing via Artillery.io", "ARC – a new weapon against accessibility bugs", "Setting up your project on GCP fast using Terraform and Kubernetes", "ngx-bootstrap v7.0.0 is officially released!", "How to deploy Firebase Preview Channels on Travis CI", "Scully Helped us Reach a 99 Lighthouse Score for a B2C Platform", "Designing Aggregator App from A to Z – Part 2", "Designing Aggregator App from A to Z — Part 1", "Multi-highlighting for DraftJS", "New year, new Valor, new you", "Incorporating user research on live projects — Part 4", "Incorporating user research on live projects — Part 3", "Incorporating user research on live projects — Part 2", "Incorporating user research on live projects — Part 1", "Why you might want to switch to Time and Material from Fixed Price", "Cross-Site Scripting (XSS): a QA Engineer's Guide", "Career Path for a Flat-structured company", "JSON Web Token Authorization with Access and Refresh Tokens in Angular Application with Node.js Server", "Testing iOS App Vulnerabilities with Jailbreaking. Part 1.", "Benefits of Agile to Business and Team", "Valor Software Named Top Development Company in Ukraine by Clutch", "Testing with Protractor: how to fix synchronization issues", "Hacktoberfest 2019 is coming, and ngx-bootstrap strives for your attention!", "The 4 biggest lessons we learned while building a startup product as an outsource company", "Angular and SEO: Structured Data for Rich Snippets", "ngx-bootstrap: Angular Ivy is here", "Webflow custom code snippets you would use. Episode 1", "Testing Canvas could be easier", "Case study of Applitools or not only Cypress cross-browser testing", "Figma as a presentation tool: Unifying templates", "Cypress testing: Running tests in parallel", "Quality Assured: What It Really Takes To Test Open Source Libraries", "Figma Vs Sketch: Text alignment comparison", "Como o Scully nos ajudou a atingir 99 pontos no Lighthouse para uma plataforma B2C"]; +const orderNumberForNextArticle = 86; \ No newline at end of file diff --git a/apps/valor-software-site/src/assets/articles/designing-aggregator-app-from-a-to-z-part-1/designing-aggregator-app-from-a-to-z-part-1.html b/apps/valor-software-site/src/assets/articles/designing-aggregator-app-from-a-to-z-part-1/designing-aggregator-app-from-a-to-z-part-1.html index 55e1b5618..3b2b7b737 100644 --- a/apps/valor-software-site/src/assets/articles/designing-aggregator-app-from-a-to-z-part-1/designing-aggregator-app-from-a-to-z-part-1.html +++ b/apps/valor-software-site/src/assets/articles/designing-aggregator-app-from-a-to-z-part-1/designing-aggregator-app-from-a-to-z-part-1.html @@ -236,7 +236,8 @@
How she picks movies and buys tickets
She usually checks the trailer, searches for ads about the film, and considers her friends' feedback. Before she used Multiplexto pick a movie and purchase tickets as she used to live right next to this +
She usually checks the trailer, searches for ads about the film, and considers her friends' feedback. Before she used https://multiplex.ua/cinema/kharkiv/dafi +[Multiplex^] to pick a movie and purchase tickets as she used to live right next to this movie place. Now she lives downtown and prefers the Planeta Kino  app. It provides a user-friendly interface, a great variety of films, and an Apple pay payment method.
Not the fact that the solution above, as it is, matched with NextJS specific. The standard dropdown component looks too generic and is not customizable as the customer requested. -I don’t want to put my routine of the research process on your plate, but describe the final decision step by step. If you want to face with my final solution now, please look at GitHub.
+I don’t want to put my routine of the research process on your plate, but describe the final decision step by step. If you want to face with my final solution now, please look at https://github.com/buchslava/nextjs-gtrans-demo.Let’s get started with the explanation!
diff --git a/apps/valor-software-site/src/assets/articles/ngx-bootstrap-angular-ivy-is-here/ngx-bootstrap-angular-ivy-is-here.html b/apps/valor-software-site/src/assets/articles/ngx-bootstrap-angular-ivy-is-here/ngx-bootstrap-angular-ivy-is-here.html index 9ca8fa6ac..96bbfc5d2 100644 --- a/apps/valor-software-site/src/assets/articles/ngx-bootstrap-angular-ivy-is-here/ngx-bootstrap-angular-ivy-is-here.html +++ b/apps/valor-software-site/src/assets/articles/ngx-bootstrap-angular-ivy-is-here/ngx-bootstrap-angular-ivy-is-here.html @@ -261,11 +261,11 @@Vitaliy Makogon
JavaScript Developer
-Twitter
Ludmila Nesvitiy
QA Automation Engineer
-Twitter
I hope this article has given you some clarity on Artillery’s software, and helped you make a choice regarding the performance testing tool you want to work with.
+In case you’re looking for help in software testing, or your project needs an advanced quality assurance pipeline +– drop us a line!
+Here I shared our first experience and thus impressions from the technology. -Hopefully, you’ll find the story useful.
+Hopefully, you’ll find the story useful. Please, don’t hesitate to share your feedback, give advice, or +contact Valor Software to give your business a boost!Hi, dear Tauri! Long time no see. I published my first post, Developing a Desktop Application via Rust and NextJS. The Tauri Way almost a year ago. Since then, Tauri has become stronger. I’m happy about that! And now, I am very pleased to make a useful contribution to the Tauri community. As a full-stack developer, I frequently face situations where I need to start a DB-based UI project as fast as possible. It’s stressful if I need to start the project from 100% scratch. I prefer to keep some boilerplates on hand, which will save me time and nerves and will be the subject of this article.
+I want to present you with my first version of the Tauri CRUD Boilerplate, which will help you bootstrap and prototype a new Tauri project from scratch. Let me focus on explanations in a What-Where-When style.
+My Tauri CRUD Boilerplate is not a silver bullet; it’s just a set of valuable scratches that help you build and connect the DB and UI parts via Tauri. Also, the Tauri CRUD Boilerplate is useful primarily for fast prototyping because it contains React and Ant Design as UI players inside, and, of course, you can substitute them with another library or framework or even provide your custom solution instead. If we focus on the DB part, I use SQLite due to its simplicity and portability. In the future, you can change SQLite. As a roadmap, I’ll think about the second version of the bootstrap when DB will be substituted with the equivalent REST API plus authentication.
+Let’s get started.
+There are a couple of tables, person and todo.
+CREATE TABLE IF NOT EXISTS person (
+ id INTEGER PRIMARY KEY NOT NULL,
+ name VARCHAR(250) NOT NULL
+);
+CREATE TABLE IF NOT EXISTS todo (
+ id INTEGER PRIMARY KEY NOT NULL,
+ date VARCHAR(20) NOT NULL,
+ notes TEXT,
+ person_id INTEGER NOT NULL,
+ completed INTEGER NOT NULL,
+ FOREIGN KEY(person_id) REFERENCES person(id)
+);
+As we can see, each person can contain a set of todos. person_id is a foreign key here.
+I implemented the following functionality.
+Data management (see Data tab below). The main aim is to create an empty SQLite DB (according to the definitions above) and fill it with the test data.
+The Persons tab contains a table that shows a set of persons and provides functionality for adding, editing, and deleting them.
+The Todos tab contains two related widgets: Persons as a read-only list and a table that shows a set of person-related todos. It provides functionality for adding, editing, and deleting them.
+Let me highlight some essential points in the Data tab code.
+You can find Create DB-related DB code here(#L6-L38) and UI code here(#L25).
+You can find Fill DB-related DB code here(#L26) and UI code here(#L26).
+It’s important to have test data and the ability to recreate it when needed (via Create DB). If you want to remove the DB, you can just delete a related file.
+It’s time to know how the connection with the DB works. First, we need to get the SQLite DB path(#L7)
+fn get_database_path() -> io::Result<PathBuf> {
+ let mut exe = env::current_exe()?;
+ exe.set_file_name("./db");
+ #[cfg(dev)]
+ exe.set_file_name("../../../db");
+ Ok(exe)
+}
+In production mode, we use the current folder to keep the DB, and the DB will be kept in the same folder as the executable file. But in the case of the debug mode, I prefer to keep the database file at the root of the project. Because the executable file in debug mode is placed in [project folder]/src-tauri/target/debug, we conditionally use the ../../../ path that’s equal to the project folder in debug mode. Fortunately, the cfg attribute helps us to do that. BTW, you can change this logic if needed.
+After we have a DB part, we need to get a path as a string
+pub fn get_database() -> String {
+ let db_url = match get_database_path() {
+ Ok(path) => path.into_os_string().into_string().unwrap(),
+ Err(e) => e.to_string(),
+ };
+ return db_url;
+}
+We use the util function above in other functions the following way. For example, when we need to insert a new record into the person table(#L14).
+#[tauri::command]
+pub async fn person_insert(name: &str) -> Result<i64, String> {
+ // get DB url as a string
+ let db_url = util::db::get_database();
+ // Connect with the DB; get connection from the pool
+ let db = SqlitePool::connect(&db_url).await.unwrap();
+ // Do all needed DB stuff
+ let query_result = sqlx
+ ::query("INSERT INTO person (name) VALUES (?)")
+ .bind(name)
+ .execute(&db).await;
+ if query_result.is_err() {
+ db.close().await;
+ return Err(format!("{:?}", query_result.err()));
+ }
+
+ let id = query_result.unwrap().last_insert_rowid();
+ // Close the connection
+ db.close().await;
+ Ok(id)
+}
+It’s time to understand how our first CRUD works on the example of Persons.
+Let’s dig into the UI part. You can read full text of the component here.
+Also, let’s dig into the data loading(#L41-L58). Please, read comment in the following code.
+const load = async () => {
+ try {
+ // get the data
+ const result = await apiCall("person_select");
+ // make it as a JSON
+ const items = JSON.parse(JSON.parse(result as unknown as string));
+ // set the related React state variable
+ setData(
+ items.map((item: any) => ({
+ key: item.id,
+ ...item,
+ }))
+ );
+ } catch (e) {
+ console.error(e);
+ errorMessage.open({
+ type: "error",
+ content: "Can't load the person list",
+ });
+ }
+ };
+Let’s focus on apiCall function. This function is important because it’s a link between React and Rust parts.
+import { InvokeArgs, invoke } from "@tauri-apps/api/tauri";
+
+export const apiCall = async <T>(
+ name: string,
+ parameters?: InvokeArgs
+): Promise<T> =>
+ new Promise((resolve, reject) =>
+ invoke(name, parameters)
+ .then(resolve as (value: unknown) => PromiseLike<T>)
+ .catch(reject)
+ );
+We import invoke function from @tauri-apps/api/tauri and just call the following functionality in Rust part. In this example we are talking about person_select(#L61-L76)
+#[tauri::command]
+pub async fn person_select() -> Result<String, String> {
+ let db_url = util::db::get_database();
+ let db = SqlitePool::connect(&db_url).await.unwrap();
+ let query_result = sqlx
+ ::query_as::<_, Person>("SELECT id, name FROM person ORDER BY id DESC")
+ .fetch_all(&db).await;
+ if query_result.is_err() {
+ db.close().await;
+ return Err(format!("{:?}", query_result.err()));
+ }
+ let results = query_result.unwrap();
+ let encoded_message = serde_json::to_string(&results).unwrap();
+ db.close().await;
+ Ok(format!("{:?}", encoded_message))
+}
+Also, I’d like to focus your attention on the following data structure(#L7-L11).
+#[derive(Serialize, Clone, FromRow, Debug)]
+pub struct Person {
+ id: i64,
+ name: String,
+}
+After that, I intend to be brief because I don’t want to waste your time. That’s why I am providing you with some significant points regarding the code.
+Let’s look at Insert and Edit…
+Press New Person(#L140) button or Edit(#L80) button on each row of data.
+Both of addNewRow(#L130) and doEdit(#L36-L39) works with the modal window here(#L36-L39) that use PersonEdit component.
+The following logic(#L103-L125) works when the form from the component above has been submitted. Its main goal is to make all expected changes (call Rust code), hide the modal window, or show the error if it is not OK.
+Todos functionality is a bit more complicated than Persons because it contains Persons and is represented by TodoContainer component that you see below. Let’s focus on how TodoContainer works.
+It based on a Antd’s Tab(#L39) component.
+Tab’s items are persons(#L14).
+Each Tab-person has its own Todo(#L20) table that also contains adding and editing functionalities.
+Focusing on the Todo and TodoEdit components doesn’t matter because their logic is similar to that of Person and PersonEdit.
+I want to stop discussing the code and focus on some practical aspects. Let’s run the app in dev mode.
+Traditionally,
+npm ci
+Run in dev mode
+npm run tauri dev
+In production mode:
+npm run tauri build
+You can found:
+An installer-based (dmg) app in [project root]/src-tauri/target/release/bundle/dmg if you want to run the installer +Or just the app in [project root]/src-tauri/target/release/bundle/macos if you want to run the app directly
+The app’s most tricky part concerns data synchronization. Imagine we just added a new person to the Persons tab and moved them to the Todo tab. The person list on the left side should be refreshed, shouldn’t it? Let me share some thoughts about data synchronization.
+I provided a context that contains the following data.
+*https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src/GlobalContext.tsx[refreshDescriptor(#L9), window=_blank] that contains a couple of fields person(#L4) and todo(#L5). +* A set of methods for the state refreshing: refreshPerson(#L10) and refreshTodo(#L11)
+import React, { createContext, useContext, useState } from "react";
+
+export interface DataRefreshDescriptor {
+ person: Date;
+ todo: Date;
+}
+
+type GlobalContextProps = {
+ refreshDescriptor: DataRefreshDescriptor;
+ refreshPerson: () => void;
+ refreshTodo: () => void;
+};
+
+const GlobalContext = createContext({} as GlobalContextProps);
+
+export type TargetKey = React.MouseEvent | React.KeyboardEvent | string;
+
+export const GlobalProvider = ({ children }: { children: React.ReactNode }) => {
+ const now = new Date();
+ const [refreshDescriptor, setRefreshDescriptor] =
+ useState<DataRefreshDescriptor>({
+ person: now,
+ todo: now,
+ });
+
+ const refreshPerson = () => {
+ setRefreshDescriptor({ ...refreshDescriptor, person: new Date() });
+ };
+
+ const refreshTodo = () => {
+ setRefreshDescriptor({ ...refreshDescriptor, todo: new Date() });
+ };
+
+ return (
+ <GlobalContext.Provider
+ value={{
+ refreshDescriptor,
+ refreshPerson,
+ refreshTodo,
+ }}
+ >
+ {children}
+ </GlobalContext.Provider>
+ );
+};
+
+export const useGlobalContext = () => useContext(GlobalContext);
+The following fragment of code illustrates us how to use the refreshDescriptor. Please, read comments there.
+// let's skip imports
+export default function TodoContainer() {
+ // get the refreshDescriptor
+ const { refreshDescriptor } = useGlobalContext();
+ const [errorMessage, errorMessageHolder] = message.useMessage();
+ const [tabs, setTabs] = useState<TabsProps["items"]>([]);
+
+ const loadPersons = async () => {
+ // let's skip the details
+ };
+
+ useEffect(() => {
+ // if refreshDescriptor.person has been changed, we need to reload the person list
+ loadPersons();
+ }, [refreshDescriptor.person]);
+
+ return (
+ <>
+ {errorMessageHolder}
+ <Tabs tabPosition={"left"} items={tabs} />
+ </>
+ );
+}
+Please, look at another example of refreshing. You can find the full version here. Please, read comments.
+// imports ...
+
+export default function Person() {
+ const { refreshDescriptor, refreshPerson } = useGlobalContext();
+ // skip...
+
+ const doDelete = async (id: number) => {
+ try {
+ await apiCall("person_delete", { id });
+ // change the 'person' field of the descriptor if a new person was deleted
+ refreshPerson();
+ } catch (e) {
+ // skip...
+ }
+ };
+
+ // skip...
+
+ const load = async () => {
+ // skip...
+ };
+
+ useEffect(() => {
+ load();
+ const columns: ColumnsType<DataType> = [
+ /// skip...
+ ];
+ setColumns(columns);
+ }, [refreshDescriptor.person]);
+
+ const doEditOk = () => {
+ editFormRef.current.submit();
+ };
+
+ const handleEditOk = async (formData: DataType) => {
+ try {
+ if (currentRecord?.id) {
+ await apiCall("person_update", {
+ name: formData.name,
+ id: currentRecord.id,
+ });
+ } else {
+ await apiCall("person_insert", {
+ name: formData.name,
+ });
+ }
+ setEditVisible(false);
+ // change the 'person' field of the descriptor if a new person was added or changed
+ refreshPerson();
+ } catch (e) {
+ setEditVisible(false);
+ // skip...
+ }
+ };
+
+ // skip...
+
+ return !columns ? (
+ <></>
+ ) : (
+ <div>
+ {errorMessageHolder}
+ <Button onClick={addNewRow} style={{ margin: 10 }}>
+ New Person
+ </Button>
+ <Table
+ columns={columns}
+ dataSource={data}
+ pagination={false}
+ scroll={{ y: "calc(100vh - 200px)" }}
+ />
+ <Modal
+ title="Person"
+ open={editVisible}
+ onOk={doEditOk}
+ onCancel={handleEditCancel}
+ >
+ <PersonEdit
+ ref={editFormRef}
+ currentRecord={currentRecord}
+ handleEditOk={handleEditOk}
+ ></PersonEdit>
+ </Modal>
+ </div>
+ );
+}
+We call refreshPerson if we expect another or the current widget refreshing. If you add some new functionality in the future, you need to:
+add new fields to refreshDescriptor
+provide the related functions and add them to the context
+use them the way described above
+I suppose it’s the simplest part of the solution.
+import { Tabs, TabsProps } from "antd";
+import "./App.css";
+import Person from "./Person";
+import TodoContainer from "./TodoContainer";
+import Data from "./Data";
+import { useGlobalContext } from "./GlobalContext";
+
+const tabs: TabsProps["items"] = [
+ {
+ key: "persons",
+ label: "Persons",
+ children: <Person />,
+ },
+ {
+ key: "todo",
+ label: "Todo",
+ children: <TodoContainer />,
+ },
+ {
+ key: "data",
+ label: "Data",
+ children: <Data />,
+ },
+];
+
+function App() {
+ const { refreshPerson, refreshTodo } = useGlobalContext();
+
+ return (
+ <Tabs
+ items={tabs}
+ onTabClick={(key: string) => {
+ if (key === "persons") {
+ refreshPerson();
+ }
+ if (key === "todo") {
+ refreshTodo();
+ }
+ }}
+ />
+ );
+}
+
+export default App;
+Please pay attention to the onTabClick handler. We change the related fields of refreshDescriptor every time we open a related tab.
+This part is essential because all that I just told you should be continued with your, my dear reader, part. I aim to summarize and guide you regarding your future steps with the boilerplate. Of course, all that I intend to share with you is not dogma; it’s just my subjective opinion that I hope will help you.
+I recommend starting with the Rust part.
+You could create a separate rs-file. This is a good example. Also, don’t forget to add it to mod.rs
+After you need to make the following changes in main.rs: use the module(#L6) and tell Tauri about the new functionality(#L16-L19). That’s it regarding Rust-part. It’s time to code in React.
+Create a table-based component like the following
+Create a form-based component like the following
+Use the table-based component in the following way(#L12)
+PS: It’s important to note that the solution above is just one of my first attempts at the topic; therefore, do not judge strictly. Also, I’d like to know how this solution works under Windows. Anyway, happy hacking, guys ;)
+We are grateful to the Clutch team and our clients for making this award possible!
+Please contact us today if you’d like to start a project.
You’ve got to have enough vision and responsibility to take care about the way your solution is born and -developed.
+developed. Follow the link to get a fact-driven estimate from my colleagues at Valor Software: https://valor-software.com/contact.htmlSpecial thanks to contributors and reviewers of the article: Dmitriy Shekhovtsov, Zackary Chapple, Nikita Demchenko, Viktoria Lurye.
diff --git a/apps/valor-software-site/src/assets/scully-routes.json b/apps/valor-software-site/src/assets/scully-routes.json index 8bfd4de44..e153cfa81 100644 --- a/apps/valor-software-site/src/assets/scully-routes.json +++ b/apps/valor-software-site/src/assets/scully-routes.json @@ -1 +1 @@ -[{"route":"/404"},{"route":"/"},{"route":"/press-release/rs-pack-rust-based-web-bundler"},{"route":"/press-release/nestjs-rspack-cicd-performance"},{"route":"/services"},{"route":"/services/quality-assurance"},{"route":"/services/devops"},{"route":"/services/product-ownership-and-project-management"},{"route":"/services/support-&-Maintenance"},{"route":"/services/design"},{"route":"/services/software-engineering"},{"route":"/clients/startups"},{"route":"/clients/smbs"},{"route":"/clients/enterprises"},{"route":"/clients/non-profit"},{"route":"/clients"},{"route":"/careers"},{"route":"/articles"},{"route":"/articles/painless-cli-integration-testing"},{"route":"/articles/painless-cli-integration-testing.html"},{"route":"/articles/google-translate-customization-2-under-nextjs"},{"route":"/articles/google-translate-customization-2-under-nextjs.html"},{"route":"/articles/google-translate-customization-under-nextjs"},{"route":"/articles/google-translate-customization-under-nextjs.html"},{"route":"/articles/exploring-asynchronous-programming-approaches-in-python-mastering-asynchronous-programming-in-python"},{"route":"/articles/exploring-asynchronous-programming-approaches-in-python-mastering-asynchronous-programming-in-python.html"},{"route":"/articles/the-interview-under-the-hood"},{"route":"/articles/the-interview-under-the-hood.html"},{"route":"/articles/game-n-qwik-the-final-episode"},{"route":"/articles/game-n-qwik-the-final-episode.html"},{"route":"/articles/game-n-qwik-episode-03"},{"route":"/articles/game-n-qwik-episode-03.html"},{"route":"/articles/game-n-qwik-episode-02"},{"route":"/articles/game-n-qwik-episode-02.html"},{"route":"/articles/game-n-qwik-episode-01"},{"route":"/articles/game-n-qwik-episode-01.html"},{"route":"/articles/fostering-innovation-through-collaboration-outside-contributors-first-hand-experience"},{"route":"/articles/fostering-innovation-through-collaboration-outside-contributors-first-hand-experience.html"},{"route":"/articles/developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way"},{"route":"/articles/developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way.html"},{"route":"/articles/qwik-reaches-the-v1-so-does-qwik-nx"},{"route":"/articles/qwik-reaches-the-v1-so-does-qwik-nx.html"},{"route":"/articles/the-evolution-of-web-application-development"},{"route":"/articles/the-evolution-of-web-application-development.html"},{"route":"/articles/zero-cost-way-on-react-d3"},{"route":"/articles/zero-cost-way-on-react-d3.html"},{"route":"/articles/multi-threading-for-impatient-rust-learners"},{"route":"/articles/multi-threading-for-impatient-rust-learners.html"},{"route":"/articles/module-federation-v7-featuring-delegate-modules-part-2"},{"route":"/articles/module-federation-v7-featuring-delegate-modules-part-2.html"},{"route":"/articles/node-rust-friendship-forever-the-napi-rs-way"},{"route":"/articles/node-rust-friendship-forever-the-napi-rs-way.html"},{"route":"/articles/module-federation-v7-featuring-delegate-modules"},{"route":"/articles/module-federation-v7-featuring-delegate-modules.html"},{"route":"/articles/design-processes-automation-with-ai-services"},{"route":"/articles/design-processes-automation-with-ai-services.html"},{"route":"/articles/react-native-and-codepush"},{"route":"/articles/react-native-and-codepush.html"},{"route":"/articles/monitoring-the-progress-of-an-http-request-in-nestjs-via-websockets"},{"route":"/articles/monitoring-the-progress-of-an-http-request-in-nestjs-via-websockets.html"},{"route":"/articles/leveraging-angular-15-host-directives"},{"route":"/articles/leveraging-angular-15-host-directives.html"},{"route":"/articles/module-federation-with-ssr-and-react-18"},{"route":"/articles/module-federation-with-ssr-and-react-18.html"},{"route":"/articles/tackling-breaking-changes-using-nx-workspace-generators"},{"route":"/articles/tackling-breaking-changes-using-nx-workspace-generators.html"},{"route":"/articles/medusa-the-new-year-dedicated-release"},{"route":"/articles/medusa-the-new-year-dedicated-release.html"},{"route":"/articles/avoiding-common-pitfalls-with-controlvalueaccessors-in-angular"},{"route":"/articles/avoiding-common-pitfalls-with-controlvalueaccessors-in-angular.html"},{"route":"/articles/migrating-a-repo-to-an-nx-monorepo-while-retaining-history"},{"route":"/articles/migrating-a-repo-to-an-nx-monorepo-while-retaining-history.html"},{"route":"/articles/valor-software-medusa-app-and-github"},{"route":"/articles/valor-software-medusa-app-and-github.html"},{"route":"/articles/a-qwik-view-of-the-ranking-bar"},{"route":"/articles/a-qwik-view-of-the-ranking-bar.html"},{"route":"/articles/rendering-nativescript-angular-templates-and-components-into-images"},{"route":"/articles/rendering-nativescript-angular-templates-and-components-into-images.html"},{"route":"/articles/medusa-resources-overlimit-in-realtime-notification"},{"route":"/articles/medusa-resources-overlimit-in-realtime-notification.html"},{"route":"/articles/modernization-with-module-federation-nx-and-react"},{"route":"/articles/modernization-with-module-federation-nx-and-react.html"},{"route":"/articles/module-federation-for-the-business"},{"route":"/articles/module-federation-for-the-business.html"},{"route":"/articles/tasty-recipes-for-react-d3-the-ranking-bar"},{"route":"/articles/tasty-recipes-for-react-d3-the-ranking-bar.html"},{"route":"/articles/introduction-to-jetpack-compose-for-nativescript"},{"route":"/articles/introduction-to-jetpack-compose-for-nativescript.html"},{"route":"/articles/module-federation-in-mobile-apps-powered-by-nativescript"},{"route":"/articles/module-federation-in-mobile-apps-powered-by-nativescript.html"},{"route":"/articles/webpack-plugin-written-by-chatgpt"},{"route":"/articles/webpack-plugin-written-by-chatgpt.html"},{"route":"/articles/introduction-to-swiftui-for-nativescript"},{"route":"/articles/introduction-to-swiftui-for-nativescript.html"},{"route":"/articles/change-is-the-only-way-to-stay-your-true-self"},{"route":"/articles/change-is-the-only-way-to-stay-your-true-self.html"},{"route":"/articles/turbopack-new-hotness-or-promising-alpha"},{"route":"/articles/turbopack-new-hotness-or-promising-alpha.html"},{"route":"/articles/medusa-the-only-commercial-available-saas-platform-for-federated-applications"},{"route":"/articles/medusa-the-only-commercial-available-saas-platform-for-federated-applications.html"},{"route":"/articles/orchestrate-your-frontend-components-with-visual-dependency-graphs"},{"route":"/articles/orchestrate-your-frontend-components-with-visual-dependency-graphs.html"},{"route":"/articles/next-js-module-federation-and-module-federation-ssr-plugins-are-being-open-sourced"},{"route":"/articles/next-js-module-federation-and-module-federation-ssr-plugins-are-being-open-sourced.html"},{"route":"/articles/nx-next-js-e-module-federation"},{"route":"/articles/nx-next-js-e-module-federation.html"},{"route":"/articles/nx-next-js-and-module-federation"},{"route":"/articles/nx-next-js-and-module-federation.html"},{"route":"/articles/debugging-ngrx-in-nativescript-with-redux-devtools"},{"route":"/articles/debugging-ngrx-in-nativescript-with-redux-devtools.html"},{"route":"/articles/announcing-strategic-partnership-with-zack-jackson-the-module-federation-inventor"},{"route":"/articles/announcing-strategic-partnership-with-zack-jackson-the-module-federation-inventor.html"},{"route":"/articles/implementing-websockets-plugin-for-nativescript-using-react-native"},{"route":"/articles/implementing-websockets-plugin-for-nativescript-using-react-native.html"},{"route":"/articles/tagtide-library-make-your-html-editor-friendly-and-more"},{"route":"/articles/tagtide-library-make-your-html-editor-friendly-and-more.html"},{"route":"/articles/diving-into-seeking-issue-with-mp3-files"},{"route":"/articles/diving-into-seeking-issue-with-mp3-files.html"},{"route":"/articles/performance-testing-via-artillery-io"},{"route":"/articles/performance-testing-via-artillery-io.html"},{"route":"/articles/arc-a-new-weapon-against-accessibility-bugs"},{"route":"/articles/arc-a-new-weapon-against-accessibility-bugs.html"},{"route":"/articles/setting-up-your-project-on-gcp-fast-using-terraform-and-kubernetes"},{"route":"/articles/setting-up-your-project-on-gcp-fast-using-terraform-and-kubernetes.html"},{"route":"/articles/ngx-bootstrap-v7-0-0-is-officially-released"},{"route":"/articles/ngx-bootstrap-v7-0-0-is-officially-released.html"},{"route":"/articles/how-to-deploy-firebase-preview-channels-on-travis-ci"},{"route":"/articles/how-to-deploy-firebase-preview-channels-on-travis-ci.html"},{"route":"/articles/scully-helped-us-reach-a-99-lighthouse-score-for-a-b2c-platform"},{"route":"/articles/scully-helped-us-reach-a-99-lighthouse-score-for-a-b2c-platform.html"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-2"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-2.html"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-1"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-1.html"},{"route":"/articles/multi-highlighting-for-draftjs"},{"route":"/articles/multi-highlighting-for-draftjs.html"},{"route":"/articles/new-year-new-valor-new-you"},{"route":"/articles/new-year-new-valor-new-you.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-4"},{"route":"/articles/incorporating-user-research-on-live-projects-part-4.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-3"},{"route":"/articles/incorporating-user-research-on-live-projects-part-3.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-2"},{"route":"/articles/incorporating-user-research-on-live-projects-part-2.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-1"},{"route":"/articles/incorporating-user-research-on-live-projects-part-1.html"},{"route":"/articles/why-you-might-want-to-switch-to-time-and-material-from-fixed-price"},{"route":"/articles/why-you-might-want-to-switch-to-time-and-material-from-fixed-price.html"},{"route":"/articles/cross-site-scripting-xss-a-qa-engineers-guide"},{"route":"/articles/cross-site-scripting-xss-a-qa-engineers-guide.html"},{"route":"/articles/career-path-for-a-flat-structured-company"},{"route":"/articles/career-path-for-a-flat-structured-company.html"},{"route":"/articles/json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server"},{"route":"/articles/json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server.html"},{"route":"/articles/testing-ios-app-vulnerabilities-with-jailbreaking-part-1"},{"route":"/articles/testing-ios-app-vulnerabilities-with-jailbreaking-part-1.html"},{"route":"/articles/benefits-of-agile-to-business-and-team"},{"route":"/articles/benefits-of-agile-to-business-and-team.html"},{"route":"/articles/valor-software-named-top-development-company-in-ukraine-by-clutch"},{"route":"/articles/valor-software-named-top-development-company-in-ukraine-by-clutch.html"},{"route":"/articles/testing-with-protractor-how-to-fix-synchronization-issues"},{"route":"/articles/testing-with-protractor-how-to-fix-synchronization-issues.html"},{"route":"/articles/hacktoberfest-2019-is-coming-and-ngx-bootstrap-strives-for-your-attention"},{"route":"/articles/hacktoberfest-2019-is-coming-and-ngx-bootstrap-strives-for-your-attention.html"},{"route":"/articles/the-4-biggest-lessons-we-learned-while-building-a-startup-product-as-an-outsource-company"},{"route":"/articles/the-4-biggest-lessons-we-learned-while-building-a-startup-product-as-an-outsource-company.html"},{"route":"/articles/angular-and-seo-structured-data-for-rich-snippets"},{"route":"/articles/angular-and-seo-structured-data-for-rich-snippets.html"},{"route":"/articles/ngx-bootstrap-angular-ivy-is-here"},{"route":"/articles/ngx-bootstrap-angular-ivy-is-here.html"},{"route":"/articles/webflow-custom-code-snippets-you-would-use-episode-1"},{"route":"/articles/webflow-custom-code-snippets-you-would-use-episode-1.html"},{"route":"/articles/testing-canvas-could-be-easier"},{"route":"/articles/testing-canvas-could-be-easier.html"},{"route":"/articles/case-study-of-applitools-or-not-only-cypress-cross-browser-testing"},{"route":"/articles/case-study-of-applitools-or-not-only-cypress-cross-browser-testing.html"},{"route":"/articles/figma-as-a-presentation-tool-unifying-templates"},{"route":"/articles/figma-as-a-presentation-tool-unifying-templates.html"},{"route":"/articles/cypress-testing-running-tests-in-parallel"},{"route":"/articles/cypress-testing-running-tests-in-parallel.html"},{"route":"/articles/quality-assured-what-it-really-takes-to-test-open-source-libraries"},{"route":"/articles/quality-assured-what-it-really-takes-to-test-open-source-libraries.html"},{"route":"/articles/figma-vs-sketch-text-alignment-comparison"},{"route":"/articles/figma-vs-sketch-text-alignment-comparison.html"},{"route":"/articles/como-o-scully-nos-ajudou-a-atingir-99-pontos-no-lighthouse-para-uma-plataforma-b2c"},{"route":"/articles/como-o-scully-nos-ajudou-a-atingir-99-pontos-no-lighthouse-para-uma-plataforma-b2c.html"},{"route":"/articles/career-path-for-a-flat-structured-sompany"},{"route":"/articles/testing-with-protractor-how-to-fix-synchroniza"},{"route":"/articles/the-partnership-press-release-zack-jackson-and-valor-software.html"},{"route":"/articles/Debugging_NgRx_in_NativeScript_with_Redux_DevTools"},{"route":"/projects"},{"route":"/projects/booking"},{"route":"/projects/terminus"},{"route":"/projects/ashes-of-creation"},{"route":"/projects/dollar-street"},{"route":"/projects/tablesready"},{"route":"/projects/liberty-flights"},{"route":"/projects/breethe"},{"route":"/projects/booking"},{"route":"/projects/booking.html"},{"route":"/projects/ashes-of-creation"},{"route":"/projects/ashes-of-creation.html"},{"route":"/projects/terminus"},{"route":"/projects/terminus.html"},{"route":"/projects/careerbuilder"},{"route":"/projects/careerbuilder.html"},{"route":"/projects/zuora"},{"route":"/projects/zuora.html"},{"route":"/projects/dollar-street"},{"route":"/projects/dollar-street.html"},{"route":"/projects/liberty-flights"},{"route":"/projects/liberty-flights.html"},{"route":"/projects/souqalmal"},{"route":"/projects/souqalmal.html"},{"route":"/projects/priceshredder"},{"route":"/projects/priceshredder.html"},{"route":"/projects/gapminder-offline"},{"route":"/projects/gapminder-offline.html"},{"route":"/projects/tablesready"},{"route":"/projects/tablesready.html"},{"route":"/projects/breethe"},{"route":"/projects/breethe.html"},{"route":"/projects/stackblitz"},{"route":"/projects/stackblitz.html"},{"route":"/projects/renaizant"},{"route":"/projects/renaizant.html"},{"route":"/projects/cinnabon"},{"route":"/projects/cinnabon.html"},{"route":"/projects/ngtalks"},{"route":"/projects/ngtalks.html"},{"route":"/projects/qualtrax"},{"route":"/projects/qualtrax.html"},{"route":"/projects/thisdot"},{"route":"/projects/thisdot.html"},{"route":"/projects/ngatlanta"},{"route":"/projects/ngatlanta.html"},{"route":"/projects/vizabi"},{"route":"/projects/vizabi.html"},{"route":"/projects/netifi"},{"route":"/projects/netifi.html"},{"route":"/privacy-policy"},{"route":"/subscription-confirmed"},{"route":"/module-federation"}] \ No newline at end of file +[{"route":"/404"},{"route":"/"},{"route":"/press-release/rs-pack-rust-based-web-bundler"},{"route":"/press-release/nestjs-rspack-cicd-performance"},{"route":"/services"},{"route":"/services/quality-assurance"},{"route":"/services/devops"},{"route":"/services/product-ownership-and-project-management"},{"route":"/services/support-&-Maintenance"},{"route":"/services/design"},{"route":"/services/software-engineering"},{"route":"/clients/startups"},{"route":"/clients/smbs"},{"route":"/clients/enterprises"},{"route":"/clients/non-profit"},{"route":"/clients"},{"route":"/careers"},{"route":"/articles"},{"route":"/articles/tauri-crud-boilerplate"},{"route":"/articles/tauri-crud-boilerplate.html"},{"route":"/articles/painless-cli-integration-testing"},{"route":"/articles/painless-cli-integration-testing.html"},{"route":"/articles/google-translate-customization-2-under-nextjs"},{"route":"/articles/google-translate-customization-2-under-nextjs.html"},{"route":"/articles/google-translate-customization-under-nextjs"},{"route":"/articles/google-translate-customization-under-nextjs.html"},{"route":"/articles/exploring-asynchronous-programming-approaches-in-python-mastering-asynchronous-programming-in-python"},{"route":"/articles/exploring-asynchronous-programming-approaches-in-python-mastering-asynchronous-programming-in-python.html"},{"route":"/articles/the-interview-under-the-hood"},{"route":"/articles/the-interview-under-the-hood.html"},{"route":"/articles/game-n-qwik-the-final-episode"},{"route":"/articles/game-n-qwik-the-final-episode.html"},{"route":"/articles/game-n-qwik-episode-03"},{"route":"/articles/game-n-qwik-episode-03.html"},{"route":"/articles/game-n-qwik-episode-02"},{"route":"/articles/game-n-qwik-episode-02.html"},{"route":"/articles/game-n-qwik-episode-01"},{"route":"/articles/game-n-qwik-episode-01.html"},{"route":"/articles/fostering-innovation-through-collaboration-outside-contributors-first-hand-experience"},{"route":"/articles/fostering-innovation-through-collaboration-outside-contributors-first-hand-experience.html"},{"route":"/articles/developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way"},{"route":"/articles/developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way.html"},{"route":"/articles/qwik-reaches-the-v1-so-does-qwik-nx"},{"route":"/articles/qwik-reaches-the-v1-so-does-qwik-nx.html"},{"route":"/articles/the-evolution-of-web-application-development"},{"route":"/articles/the-evolution-of-web-application-development.html"},{"route":"/articles/zero-cost-way-on-react-d3"},{"route":"/articles/zero-cost-way-on-react-d3.html"},{"route":"/articles/multi-threading-for-impatient-rust-learners"},{"route":"/articles/multi-threading-for-impatient-rust-learners.html"},{"route":"/articles/module-federation-v7-featuring-delegate-modules-part-2"},{"route":"/articles/module-federation-v7-featuring-delegate-modules-part-2.html"},{"route":"/articles/node-rust-friendship-forever-the-napi-rs-way"},{"route":"/articles/node-rust-friendship-forever-the-napi-rs-way.html"},{"route":"/articles/module-federation-v7-featuring-delegate-modules"},{"route":"/articles/module-federation-v7-featuring-delegate-modules.html"},{"route":"/articles/design-processes-automation-with-ai-services"},{"route":"/articles/design-processes-automation-with-ai-services.html"},{"route":"/articles/react-native-and-codepush"},{"route":"/articles/react-native-and-codepush.html"},{"route":"/articles/monitoring-the-progress-of-an-http-request-in-nestjs-via-websockets"},{"route":"/articles/monitoring-the-progress-of-an-http-request-in-nestjs-via-websockets.html"},{"route":"/articles/leveraging-angular-15-host-directives"},{"route":"/articles/leveraging-angular-15-host-directives.html"},{"route":"/articles/module-federation-with-ssr-and-react-18"},{"route":"/articles/module-federation-with-ssr-and-react-18.html"},{"route":"/articles/tackling-breaking-changes-using-nx-workspace-generators"},{"route":"/articles/tackling-breaking-changes-using-nx-workspace-generators.html"},{"route":"/articles/medusa-the-new-year-dedicated-release"},{"route":"/articles/medusa-the-new-year-dedicated-release.html"},{"route":"/articles/avoiding-common-pitfalls-with-controlvalueaccessors-in-angular"},{"route":"/articles/avoiding-common-pitfalls-with-controlvalueaccessors-in-angular.html"},{"route":"/articles/migrating-a-repo-to-an-nx-monorepo-while-retaining-history"},{"route":"/articles/migrating-a-repo-to-an-nx-monorepo-while-retaining-history.html"},{"route":"/articles/valor-software-medusa-app-and-github"},{"route":"/articles/valor-software-medusa-app-and-github.html"},{"route":"/articles/a-qwik-view-of-the-ranking-bar"},{"route":"/articles/a-qwik-view-of-the-ranking-bar.html"},{"route":"/articles/rendering-nativescript-angular-templates-and-components-into-images"},{"route":"/articles/rendering-nativescript-angular-templates-and-components-into-images.html"},{"route":"/articles/medusa-resources-overlimit-in-realtime-notification"},{"route":"/articles/medusa-resources-overlimit-in-realtime-notification.html"},{"route":"/articles/modernization-with-module-federation-nx-and-react"},{"route":"/articles/modernization-with-module-federation-nx-and-react.html"},{"route":"/articles/module-federation-for-the-business"},{"route":"/articles/module-federation-for-the-business.html"},{"route":"/articles/tasty-recipes-for-react-d3-the-ranking-bar"},{"route":"/articles/tasty-recipes-for-react-d3-the-ranking-bar.html"},{"route":"/articles/introduction-to-jetpack-compose-for-nativescript"},{"route":"/articles/introduction-to-jetpack-compose-for-nativescript.html"},{"route":"/articles/module-federation-in-mobile-apps-powered-by-nativescript"},{"route":"/articles/module-federation-in-mobile-apps-powered-by-nativescript.html"},{"route":"/articles/webpack-plugin-written-by-chatgpt"},{"route":"/articles/webpack-plugin-written-by-chatgpt.html"},{"route":"/articles/introduction-to-swiftui-for-nativescript"},{"route":"/articles/introduction-to-swiftui-for-nativescript.html"},{"route":"/articles/change-is-the-only-way-to-stay-your-true-self"},{"route":"/articles/change-is-the-only-way-to-stay-your-true-self.html"},{"route":"/articles/turbopack-new-hotness-or-promising-alpha"},{"route":"/articles/turbopack-new-hotness-or-promising-alpha.html"},{"route":"/articles/medusa-the-only-commercial-available-saas-platform-for-federated-applications"},{"route":"/articles/medusa-the-only-commercial-available-saas-platform-for-federated-applications.html"},{"route":"/articles/orchestrate-your-frontend-components-with-visual-dependency-graphs"},{"route":"/articles/orchestrate-your-frontend-components-with-visual-dependency-graphs.html"},{"route":"/articles/next-js-module-federation-and-module-federation-ssr-plugins-are-being-open-sourced"},{"route":"/articles/next-js-module-federation-and-module-federation-ssr-plugins-are-being-open-sourced.html"},{"route":"/articles/nx-next-js-e-module-federation"},{"route":"/articles/nx-next-js-e-module-federation.html"},{"route":"/articles/nx-next-js-and-module-federation"},{"route":"/articles/nx-next-js-and-module-federation.html"},{"route":"/articles/debugging-ngrx-in-nativescript-with-redux-devtools"},{"route":"/articles/debugging-ngrx-in-nativescript-with-redux-devtools.html"},{"route":"/articles/announcing-strategic-partnership-with-zack-jackson-the-module-federation-inventor"},{"route":"/articles/announcing-strategic-partnership-with-zack-jackson-the-module-federation-inventor.html"},{"route":"/articles/implementing-websockets-plugin-for-nativescript-using-react-native"},{"route":"/articles/implementing-websockets-plugin-for-nativescript-using-react-native.html"},{"route":"/articles/tagtide-library-make-your-html-editor-friendly-and-more"},{"route":"/articles/tagtide-library-make-your-html-editor-friendly-and-more.html"},{"route":"/articles/diving-into-seeking-issue-with-mp3-files"},{"route":"/articles/diving-into-seeking-issue-with-mp3-files.html"},{"route":"/articles/performance-testing-via-artillery-io"},{"route":"/articles/performance-testing-via-artillery-io.html"},{"route":"/articles/arc-a-new-weapon-against-accessibility-bugs"},{"route":"/articles/arc-a-new-weapon-against-accessibility-bugs.html"},{"route":"/articles/setting-up-your-project-on-gcp-fast-using-terraform-and-kubernetes"},{"route":"/articles/setting-up-your-project-on-gcp-fast-using-terraform-and-kubernetes.html"},{"route":"/articles/ngx-bootstrap-v7-0-0-is-officially-released"},{"route":"/articles/ngx-bootstrap-v7-0-0-is-officially-released.html"},{"route":"/articles/how-to-deploy-firebase-preview-channels-on-travis-ci"},{"route":"/articles/how-to-deploy-firebase-preview-channels-on-travis-ci.html"},{"route":"/articles/scully-helped-us-reach-a-99-lighthouse-score-for-a-b2c-platform"},{"route":"/articles/scully-helped-us-reach-a-99-lighthouse-score-for-a-b2c-platform.html"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-2"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-2.html"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-1"},{"route":"/articles/designing-aggregator-app-from-a-to-z-part-1.html"},{"route":"/articles/multi-highlighting-for-draftjs"},{"route":"/articles/multi-highlighting-for-draftjs.html"},{"route":"/articles/new-year-new-valor-new-you"},{"route":"/articles/new-year-new-valor-new-you.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-4"},{"route":"/articles/incorporating-user-research-on-live-projects-part-4.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-3"},{"route":"/articles/incorporating-user-research-on-live-projects-part-3.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-2"},{"route":"/articles/incorporating-user-research-on-live-projects-part-2.html"},{"route":"/articles/incorporating-user-research-on-live-projects-part-1"},{"route":"/articles/incorporating-user-research-on-live-projects-part-1.html"},{"route":"/articles/why-you-might-want-to-switch-to-time-and-material-from-fixed-price"},{"route":"/articles/why-you-might-want-to-switch-to-time-and-material-from-fixed-price.html"},{"route":"/articles/cross-site-scripting-xss-a-qa-engineers-guide"},{"route":"/articles/cross-site-scripting-xss-a-qa-engineers-guide.html"},{"route":"/articles/career-path-for-a-flat-structured-company"},{"route":"/articles/career-path-for-a-flat-structured-company.html"},{"route":"/articles/json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server"},{"route":"/articles/json-web-token-authorization-with-access-and-refresh-tokens-in-angular-application-with-node-js-server.html"},{"route":"/articles/testing-ios-app-vulnerabilities-with-jailbreaking-part-1"},{"route":"/articles/testing-ios-app-vulnerabilities-with-jailbreaking-part-1.html"},{"route":"/articles/benefits-of-agile-to-business-and-team"},{"route":"/articles/benefits-of-agile-to-business-and-team.html"},{"route":"/articles/valor-software-named-top-development-company-in-ukraine-by-clutch"},{"route":"/articles/valor-software-named-top-development-company-in-ukraine-by-clutch.html"},{"route":"/articles/testing-with-protractor-how-to-fix-synchronization-issues"},{"route":"/articles/testing-with-protractor-how-to-fix-synchronization-issues.html"},{"route":"/articles/hacktoberfest-2019-is-coming-and-ngx-bootstrap-strives-for-your-attention"},{"route":"/articles/hacktoberfest-2019-is-coming-and-ngx-bootstrap-strives-for-your-attention.html"},{"route":"/articles/the-4-biggest-lessons-we-learned-while-building-a-startup-product-as-an-outsource-company"},{"route":"/articles/the-4-biggest-lessons-we-learned-while-building-a-startup-product-as-an-outsource-company.html"},{"route":"/articles/angular-and-seo-structured-data-for-rich-snippets"},{"route":"/articles/angular-and-seo-structured-data-for-rich-snippets.html"},{"route":"/articles/ngx-bootstrap-angular-ivy-is-here"},{"route":"/articles/ngx-bootstrap-angular-ivy-is-here.html"},{"route":"/articles/webflow-custom-code-snippets-you-would-use-episode-1"},{"route":"/articles/webflow-custom-code-snippets-you-would-use-episode-1.html"},{"route":"/articles/testing-canvas-could-be-easier"},{"route":"/articles/testing-canvas-could-be-easier.html"},{"route":"/articles/case-study-of-applitools-or-not-only-cypress-cross-browser-testing"},{"route":"/articles/case-study-of-applitools-or-not-only-cypress-cross-browser-testing.html"},{"route":"/articles/figma-as-a-presentation-tool-unifying-templates"},{"route":"/articles/figma-as-a-presentation-tool-unifying-templates.html"},{"route":"/articles/cypress-testing-running-tests-in-parallel"},{"route":"/articles/cypress-testing-running-tests-in-parallel.html"},{"route":"/articles/quality-assured-what-it-really-takes-to-test-open-source-libraries"},{"route":"/articles/quality-assured-what-it-really-takes-to-test-open-source-libraries.html"},{"route":"/articles/figma-vs-sketch-text-alignment-comparison"},{"route":"/articles/figma-vs-sketch-text-alignment-comparison.html"},{"route":"/articles/como-o-scully-nos-ajudou-a-atingir-99-pontos-no-lighthouse-para-uma-plataforma-b2c"},{"route":"/articles/como-o-scully-nos-ajudou-a-atingir-99-pontos-no-lighthouse-para-uma-plataforma-b2c.html"},{"route":"/articles/career-path-for-a-flat-structured-sompany"},{"route":"/articles/testing-with-protractor-how-to-fix-synchroniza"},{"route":"/articles/the-partnership-press-release-zack-jackson-and-valor-software.html"},{"route":"/articles/Debugging_NgRx_in_NativeScript_with_Redux_DevTools"},{"route":"/projects"},{"route":"/projects/booking"},{"route":"/projects/terminus"},{"route":"/projects/ashes-of-creation"},{"route":"/projects/dollar-street"},{"route":"/projects/tablesready"},{"route":"/projects/liberty-flights"},{"route":"/projects/breethe"},{"route":"/projects/booking"},{"route":"/projects/booking.html"},{"route":"/projects/ashes-of-creation"},{"route":"/projects/ashes-of-creation.html"},{"route":"/projects/terminus"},{"route":"/projects/terminus.html"},{"route":"/projects/careerbuilder"},{"route":"/projects/careerbuilder.html"},{"route":"/projects/zuora"},{"route":"/projects/zuora.html"},{"route":"/projects/dollar-street"},{"route":"/projects/dollar-street.html"},{"route":"/projects/liberty-flights"},{"route":"/projects/liberty-flights.html"},{"route":"/projects/souqalmal"},{"route":"/projects/souqalmal.html"},{"route":"/projects/priceshredder"},{"route":"/projects/priceshredder.html"},{"route":"/projects/gapminder-offline"},{"route":"/projects/gapminder-offline.html"},{"route":"/projects/tablesready"},{"route":"/projects/tablesready.html"},{"route":"/projects/breethe"},{"route":"/projects/breethe.html"},{"route":"/projects/stackblitz"},{"route":"/projects/stackblitz.html"},{"route":"/projects/renaizant"},{"route":"/projects/renaizant.html"},{"route":"/projects/cinnabon"},{"route":"/projects/cinnabon.html"},{"route":"/projects/ngtalks"},{"route":"/projects/ngtalks.html"},{"route":"/projects/qualtrax"},{"route":"/projects/qualtrax.html"},{"route":"/projects/thisdot"},{"route":"/projects/thisdot.html"},{"route":"/projects/ngatlanta"},{"route":"/projects/ngatlanta.html"},{"route":"/projects/vizabi"},{"route":"/projects/vizabi.html"},{"route":"/projects/netifi"},{"route":"/projects/netifi.html"},{"route":"/privacy-policy"},{"route":"/subscription-confirmed"},{"route":"/module-federation"}] \ No newline at end of file diff --git a/assets/articles/0085-tauri-crud-boilerplate/Slava_Chub.jpg b/assets/articles/0085-tauri-crud-boilerplate/Slava_Chub.jpg new file mode 100644 index 000000000..a730ee24f Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/Slava_Chub.jpg differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img1.png b/assets/articles/0085-tauri-crud-boilerplate/img1.png new file mode 100644 index 000000000..dbb54cbe1 Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img1.png differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img2.png b/assets/articles/0085-tauri-crud-boilerplate/img2.png new file mode 100644 index 000000000..214670eb2 Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img2.png differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img3.png b/assets/articles/0085-tauri-crud-boilerplate/img3.png new file mode 100644 index 000000000..7b1778684 Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img3.png differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img4.png b/assets/articles/0085-tauri-crud-boilerplate/img4.png new file mode 100644 index 000000000..3f96ed6ef Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img4.png differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img5.png b/assets/articles/0085-tauri-crud-boilerplate/img5.png new file mode 100644 index 000000000..72fec8a5e Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img5.png differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img6.gif b/assets/articles/0085-tauri-crud-boilerplate/img6.gif new file mode 100644 index 000000000..1111870dc Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img6.gif differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/img7.gif b/assets/articles/0085-tauri-crud-boilerplate/img7.gif new file mode 100644 index 000000000..05b9b4063 Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/img7.gif differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/main-tauri-crud-boilerplate.png b/assets/articles/0085-tauri-crud-boilerplate/main-tauri-crud-boilerplate.png new file mode 100644 index 000000000..420bbe2ae Binary files /dev/null and b/assets/articles/0085-tauri-crud-boilerplate/main-tauri-crud-boilerplate.png differ diff --git a/assets/articles/0085-tauri-crud-boilerplate/tauri-crud-boilerplate.adoc b/assets/articles/0085-tauri-crud-boilerplate/tauri-crud-boilerplate.adoc new file mode 100644 index 000000000..588fcacca --- /dev/null +++ b/assets/articles/0085-tauri-crud-boilerplate/tauri-crud-boilerplate.adoc @@ -0,0 +1,519 @@ +== Introduction +Hi, dear https://tauri.app[Tauri, window=_blank]! Long time no see. I published my first post, https://valor-software.com/articles/developing-a-desktop-application-via-rust-and-nextjs-the-tauri-way[Developing a Desktop Application via Rust and NextJS. The Tauri Way, window=_blank] almost a year ago. Since then, Tauri has become stronger. I'm happy about that! And now, I am very pleased to make a useful contribution to the Tauri community. As a full-stack developer, I frequently face situations where I need to start a DB-based UI project as fast as possible. It's stressful if I need to start the project from 100% scratch. I prefer to keep some boilerplates on hand, which will save me time and nerves and will be the subject of this article. + +I want to present you with my first version of the https://github.com/buchslava/tauri-crud-boilerplate[Tauri CRUD Boilerplate, window=_blank], which will help you bootstrap and prototype a new Tauri project from scratch. Let me focus on explanations in a What-Where-When style. + +My #Tauri CRUD Boilerplate# is not a silver bullet; it's just a set of valuable scratches that help you build and connect the DB and UI parts via Tauri. Also, the Tauri CRUD Boilerplate is useful primarily for fast prototyping because it contains React and Ant Design as UI players inside, and, of course, you can substitute them with another library or framework or even provide your custom solution instead. If we focus on the DB part, I use SQLite due to its simplicity and portability. In the future, you can change SQLite. As a roadmap, I'll think about the second version of the bootstrap when DB will be substituted with the equivalent REST API plus authentication. + +Let's get started. + +There are a couple of https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src-tauri/src/crud/data.rs[tables, window=_blank], #person# and #todo#. + +[, code] +---- +CREATE TABLE IF NOT EXISTS person ( + id INTEGER PRIMARY KEY NOT NULL, + name VARCHAR(250) NOT NULL +); +---- +[, code] +---- +CREATE TABLE IF NOT EXISTS todo ( + id INTEGER PRIMARY KEY NOT NULL, + date VARCHAR(20) NOT NULL, + notes TEXT, + person_id INTEGER NOT NULL, + completed INTEGER NOT NULL, + FOREIGN KEY(person_id) REFERENCES person(id) +); +---- + +As we can see, each person can contain a set of todos. #person_id# is a foreign key here. + +I implemented the following functionality. + +1. Data management (see #Data# tab below). The main aim is to create an empty SQLite DB (according to the definitions above) and fill it with the test data. + +2. The Persons tab contains a table that shows a set of persons and provides functionality for adding, editing, and deleting them. + +3. The Todos tab contains two related widgets: Persons as a read-only list and a table that shows a set of person-related todos. It provides functionality for adding, editing, and deleting them. + +=== Data +[.img] +image::img1.png[] + +Let me highlight some essential points in the #Data# tab code. + +* You can find #Create DB#-related DB code https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src-tauri/src/crud/data.rs[here(#L6-L38), window=_blank] and UI code https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src/Data.tsx[here(#L25), window=_blank]. +* You can find #Fill DB#-related DB code https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src/Data.tsx[here(#L26), window=_blank] and UI code https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src/Data.tsx[here(#L26), window=_blank]. + +It's important to have test data and the ability to recreate it when needed (via #Create DB#). If you want to remove the DB, you can just delete a related file. + +=== DB connect + +It's time to know how the connection with the DB works. First, we need to get the https://github.com/buchslava/tauri-crud-boilerplate/blob/devto-article/src-tauri/src/util/db.rs[SQLite DB path(#L7), window=_blank] + +[, code] +---- +fn get_database_path() -> io::Result