+
setOpen(true)}
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index f92cd3d..e093c22 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -1,5 +1,7 @@
import { useState } from "react";
import { Menu, Dropdown, Button, Image, Grid } from "antd";
+import { useSpring, animated } from "react-spring";
+import { useLocation, Link } from "react-router-dom";
import {
GithubOutlined,
QuestionOutlined,
@@ -13,9 +15,29 @@ const { useBreakpoint } = Grid;
function Navbar({ scrollToExplore }: { scrollToExplore: any }) {
const [hovered, setHovered] = useState<
- null | "home" | "explore" | "help" | "github"
+ null | "home" | "explore" | "help" | "github" | "join"
>(null);
const screens = useBreakpoint();
+ const location = useLocation();
+
+ const props = useSpring({
+ to: async (next) => {
+ while (true) {
+ await next({
+ opacity: 1,
+ boxShadow: "0px 0px 5px rgba(255, 255, 255, 1)",
+ });
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ await next({
+ opacity: 0.9,
+ boxShadow: "0px 0px 0px rgba(255, 255, 255, 0)",
+ });
+ await new Promise((resolve) => setTimeout(resolve, 4000));
+ }
+ },
+ from: { opacity: 0.5, boxShadow: "0px 0px 0px rgba(255, 255, 255, 0)" },
+ config: { duration: 1000 },
+ });
const menu = (
);
diff --git a/src/components/SampleDropdown.tsx b/src/components/SampleDropdown.tsx
index d6b8c3d..00deb7d 100644
--- a/src/components/SampleDropdown.tsx
+++ b/src/components/SampleDropdown.tsx
@@ -34,9 +34,11 @@ function SampleDropdown({ setLoading }: { setLoading: any }) {
return (
-
+
+
+
);
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
new file mode 100644
index 0000000..d139357
--- /dev/null
+++ b/src/components/Sidebar.tsx
@@ -0,0 +1,52 @@
+import React from "react";
+import { Link } from "react-router-dom";
+import {
+ SidebarContainer,
+ SidebarTitle,
+ SidebarList,
+ SidebarListItem,
+ SidebarLink,
+ HelperBox,
+ HelperIcon,
+ HelperText,
+ DividerLine,
+} from "../styles/components/Sidebar";
+import { BulbOutlined } from "@ant-design/icons";
+
+const Sidebar: React.FC<{ steps: { title: string; link: string }[] }> = ({
+ steps,
+}) => {
+ return (
+
+ Learning Pathway
+
+ {steps.map((step, index) => (
+
+ (isActive ? "active" : undefined)}
+ >
+ {step.title}
+
+
+ ))}
+
+
+
+
+
+
+
+ Welcome to the Learning Pathway! Use the sidebar to follow the guide.
+ Open the{" "}
+
+ Template Playground
+ {" "}
+ in another tab to experiment as you learn.
+
+
+
+ );
+};
+
+export default Sidebar;
diff --git a/src/components/Tour.ts b/src/components/Tour.ts
new file mode 100644
index 0000000..93ed9ca
--- /dev/null
+++ b/src/components/Tour.ts
@@ -0,0 +1,92 @@
+import Shepherd from "shepherd.js";
+import "shepherd.js/dist/css/shepherd.css";
+
+const style = document.createElement("style");
+style.textContent = `
+ .shepherd-button {
+ background-color: #050c40 !important;
+ color: white !important;
+ }
+`;
+document.head.appendChild(style);
+
+const tour = new Shepherd.Tour({
+ defaultStepOptions: {
+ classes: "shepherd-theme-arrows",
+ scrollTo: true,
+ },
+ useModalOverlay: true,
+});
+
+tour.addStep({
+ id: "intro",
+ text: "Welcome to the Template Playground! This brief tour will help you get acquainted with the key features of the platform.",
+ buttons: [
+ {
+ text: "Next",
+ action: tour.next,
+ },
+ ],
+});
+
+tour.addStep({
+ id: "samples",
+ text: "Here is the 'Templates' dropdown. This dropdown contains various templates that you can edit and experiment with. Select a template to see and modify its details.",
+ attachTo: {
+ element: ".samples-element",
+ on: "bottom",
+ },
+ buttons: [
+ {
+ text: "Next",
+ action: tour.next,
+ },
+ ],
+});
+
+tour.addStep({
+ id: "share",
+ text: "Use this 'Share' button to generate and share a link for any created or edited templates. Share your work with others easily.",
+ attachTo: {
+ element: ".share-element",
+ on: "bottom",
+ },
+ buttons: [
+ {
+ text: "Next",
+ action: tour.next,
+ },
+ ],
+});
+
+tour.addStep({
+ id: "preview",
+ text: "This section shows the live preview of your template. View the results of your edits and see how your template renders.",
+ attachTo: {
+ element: ".preview-element",
+ on: "bottom",
+ },
+ buttons: [
+ {
+ text: "Next",
+ action: tour.next,
+ },
+ ],
+});
+
+tour.addStep({
+ id: "learnNow",
+ text: 'Click the "Learn Now" button to access the Learning Pathway. Here, you will find comprehensive documentation and tutorials to help you create templates effectively.',
+ attachTo: {
+ element: ".learnNow-button",
+ on: "bottom",
+ },
+ buttons: [
+ {
+ text: "Finish Tour",
+ action: tour.cancel,
+ },
+ ],
+});
+
+export default tour;
diff --git a/src/components/UseShare.tsx b/src/components/UseShare.tsx
index 22143a4..4a81f1e 100644
--- a/src/components/UseShare.tsx
+++ b/src/components/UseShare.tsx
@@ -21,9 +21,11 @@ const UseShare = () => {
};
return (
-
} onClick={handleCopy}>
- {copied ? "Copied!" : "Share"}
-
+
+ } onClick={handleCopy}>
+ {copied ? "Copied!" : "Share"}
+
+
);
};
diff --git a/src/constants/learningSteps/steps.ts b/src/constants/learningSteps/steps.ts
new file mode 100644
index 0000000..7c53c2f
--- /dev/null
+++ b/src/constants/learningSteps/steps.ts
@@ -0,0 +1,8 @@
+// ❕ learning module steps/paths
+
+export const steps = [
+ { title: "Overview", link: "/learn/intro" },
+ { title: "Module 1", link: "/learn/module1" },
+ { title: "Module 2", link: "/learn/module2" },
+ { title: "Module 3", link: "/learn/module3" },
+];
diff --git a/src/content/intro.md b/src/content/intro.md
index f0c16a0..75bddec 100644
--- a/src/content/intro.md
+++ b/src/content/intro.md
@@ -1,51 +1,9 @@
-# Overview
+# Introduction
-The Accord Project Template Playground is an innovative web-based platform designed to empower developers and enthusiasts to explore and experiment with Accord Project functionalities in an interactive and user-friendly environment. This playground serves as your hands-on tool for working with Accord Project templates, offering a rich set of features to facilitate learning and experimentation.
+In this tutorial we will learn to draft an Accord Project smart contract template. We will draft a Hello World Accord Project Template.
-## What is the Accord Project?
+This tutorial has the following modules:
-The Accord Project is an open-source, non-profit initiative dedicated to transforming contract management and automation through digital contracts. By providing a standardized format for _Smart Legal Contracts_, the Accord Project aims to modernize traditional contract practices with a digitized approach.
-
-At the core of the Accord Project are templates that blend _natural language text_ with computing logic. These templates function similarly to clauses or contract templates but are designed to be both human-readable and machine-executable. This innovative format bridges the gap between static documentation and dynamic, interactive contract management.
-
-![AP](/public/APLogo.png)
-
-## Key Components of the Template Playground
-
-The Template Playground allows you to dive into several key Accord Project technologies:
-
-1. **TemplateMark**: This feature lets you work with natural language text within templates. TemplateMark is crucial for defining the text-based structure of contracts.
-
-2. **[Concerto](https://docs.accordproject.org/docs/model-concerto.html)**: Concerto serves as the data model that bridges the natural language text and the executable logic. It outlines the structure and data types used within the templates.
-
-3. **[Markdown-Transform](https://github.com/accordproject/markdown-transform)**: This tool transforms the template output into various formats such as HTML, PDF, etc., offering flexibility in presentation.
-
-4. **[Template-Engine](https://github.com/accordproject/template-engine)**: The Template Engine converts TemplateMark and JSON data into _AgreementMark_, a standardized format for Smart Legal Contracts.
-
-## Interactive Features
-
-The Template Playground is designed to enrich your learning experience with interactive features:
-
-- **Live Template Testing and Editing**: Edit and test template samples directly in the playground. Syntax highlighting and error checking ensure your templates are accurate and functional.
-
-- **Real-Time Preview**: View the results of your code execution instantly. The playground provides a live preview of your work, allowing you to see the output in real time.
-
-- **Shareable Links**: Share your edited templates with others effortlessly. The playground generates shareable links, enabling collaboration and showcasing your work.
-
-## Getting Started
-
-To get started with the Template Playground:
-
-1. **Select a Template**: Choose from various pre-built templates available in the dropdown menu. The "Hello World" template is an excellent starting point for beginners.
-
-2. **Experiment with TemplateMark**: Begin by exploring the natural language text of the template. Modify and observe how changes affect the output.
-
-3. **Explore the Concerto Model**: Dive into the data model supporting your template. Understand how the model integrates with the template text and logic.
-
-4. **Define Template Logic**: Implement and test the business logic for your template. Use JSON data to preview how your logic interacts with the template.
-
-5. **Preview Output**: Utilize the preview feature to view the final result of your work. The playground provides an easy way to see how your template looks and functions.
-
-## What’s Next?
-
-In the upcoming module, we will delve deeply into **_TemplateMark_**—the natural language component of templates. You’ll learn how to create and experiment with templates, exploring the structure and syntax that make TemplateMark a powerful tool for defining contracts. This hands-on experience will provide you with a solid foundation for building and understanding templates in the Accord Project.
+- [Module 1](https://playground.accordproject.org/learn/module1): Create a Concerto model for your template.
+- [Module 2](https://playground.accordproject.org/learn/module2): Draft a TemplateMark template.
+- [Module 3](https://playground.accordproject.org/learn/module3): Pass some data to the TemplateMark template conforming to the shape defined by the Concerto Model
\ No newline at end of file
diff --git a/src/content/module1.md b/src/content/module1.md
index 0fd2e8e..05f4c92 100644
--- a/src/content/module1.md
+++ b/src/content/module1.md
@@ -1,123 +1,38 @@
-# Module 1: Introduction to TemplateMark
+# Module 1
-The **TemplateMark** language is a crucial element of the Accord Project template system. It allows you to craft templates using a mix of _natural language text_ and _markup syntax_, making it possible to create documents that are both _human-readable_ and _machine-readable_. In this module, we will delve into the basics of TemplateMark, showcasing its key features and demonstrating its practical application within the Accord Project Template Playground.
+## _Create a Concerto model for your template._
-## What is TemplateMark?
+Learn more about Concerto modelling language and its runtime [here](https://concerto.accordproject.org/)
-TemplateMark is a markup language used for defining templates in the Accord Project. It combines plain text with specific notations, enabling templates to be interpreted and processed by computers while remaining understandable to humans.
+- Step 1: Open the Concerto Model editor and define the [Model Namespace](https://concerto.accordproject.org/docs/design/specification/model-namespaces).
-![Template Text](/public/assets/content/template_text.png)
-
-Templates in Accord Project are made up of three essential elements:
-
-- **Template Text**: The natural language content of the template.
-- **Template Model**: The data model that provides the connection between the text and the executable logic.
-- **Template Logic**: The business rules and logic that define how the template is processed and used.
-
-These components work together to create a powerful tool for contract automation and management.
-
-## Example of Template Text
-
-Let's explore a TemplateMark example for a **Service Agreement** clause:
-
-```js
-## Service Agreement
-
-{{provider}} agrees to provide the following services to {{client}}:
-
-- **Service 1**: {{service1Description}}
-- **Service 2**: {{service2Description}}
-
-The services will be delivered according to the following schedule:
-
-- **Start Date**: {{startDate as "D MMMM YYYY"}}
-- **End Date**: {{endDate as "D MMMM YYYY"}}
-
-Payment terms are as follows:
-
-- **Amount**: {{paymentAmount as "0,0.00 USD"}}
-- **Due Date**: {{paymentDueDate as "D MMMM YYYY"}}
-
-Any changes to the service agreement must be documented in writing and agreed upon by both parties.
```
-
-In this template:
-
-- `{{provider}}`, `{{client}}`, `{{service1Description}}`, `{{service2Description}}`, `{{startDate}}`, `{{endDate}}`, `{{paymentAmount}}`, and `{{paymentDueDate}}` are placeholders that will be replaced with actual values when the template is used.
-
-Here's how the same clause looks with placeholder values filled in:
-
-```md
-## Service Agreement
-
-"ABC Services Ltd." agrees to provide the following services to "XYZ Corp":
-
-- **Service 1**: Monthly IT support and maintenance
-- **Service 2**: On-site technical assistance
-
-The services will be delivered according to the following schedule:
-
-- **Start Date**: 1 September 2024
-- **End Date**: 31 August 2025
-
-Payment terms are as follows:
-
-- **Amount**: $5,000.00
-- **Due Date**: 1 September 2024
-
-Any changes to the service agreement must be documented in writing and agreed upon by both parties.
+namespace hello@1.0.0
```
-## Key Features of TemplateMark
-
-- **Variables**: Use `{{` and `}}` to denote placeholders in your template. These placeholders are replaced with specific values during template processing.
-- **Conditional Sections**: Incorporate or exclude sections based on conditions using `{{#clause}} ... {{/clause}}`.
-- **Formatting**: Apply formatting like bold or italics to emphasize important terms.
-- **Lists**: Organize information using ordered or unordered lists.
-
-## Practical Example in the Playground
-
-In the Template Playground, you can interact with TemplateMark templates such as:
-
-```js
-### Welcome {{username}}!
-
-![Logo](https://avatars.githubusercontent.com/u/29445438?s=64)
-
-{{#clause personalDetails}}
-#### Personal Details
-> {{firstName}} {{lastName}},
- {{address}}, {{city}}, {{state}},
- {{country}}
- {{/clause}}
+- Step 2: Define a new [Concept](https://concerto.accordproject.org/docs/design/specification/model-classes) `Hello World`
-- Your date of birth is *{{dob as "D MMMM YYYY"}}*
-- Your annual salary is {{annualSalary as "0,0.00 USD"}}
-- Your preferred contact method is {{contactMethod}}
-
-{{#clause recentActivity}}
-## Recent Activity
-You signed up on {{signupDate as "D MMMM YYYY"}} ({{% return now.diff(user.signupDate, 'day')%}} days ago).
-
-{{#ulist recentPurchases}}
-- {{quantity}}x _{{item}}_ @ ${{price as "0,0.00"}}
-{{/ulist}}
-Total spent: {{% return '£' + user.recentPurchases.map(purchase => purchase.price * purchase.quantity).reduce((sum, cur) => sum + cur).toFixed(2);%}}
-{{/clause}}
-
-Thank you for being with us!
+```
+namespace hello@1.0.0
+concept HelloWorld {
+}
```
-In this example:
-
-- **Welcome Message**: Personalize messages with dynamic values.
-- **Personal Details Clause**: Display personal information conditionally.
-- **Recent Activity**: List recent purchases with calculations.
-
-## Summary
+- Step 3: Add a new [Decorator](https://concerto.accordproject.org/docs/design/specification/model-decorators) `@template` on the Concept.
-In this module, we introduced TemplateMark, highlighting its role in creating flexible and dynamic templates. By understanding TemplateMark, you can build templates that combine natural language with computer logic, enabling sophisticated document automation and management.
+```
+namespace hello@1.0.0
+@template
+concept HelloWorld {
+}
+```
-## What's Next?
+- Step 4: Add a new [String Propoerty](https://concerto.accordproject.org/docs/design/specification/model-properties) to the Concept.
-In the next module, we'll explore the **Template Model**—the data structure that supports and enhances the TemplateMark text. We'll cover how the Template Model integrates with TemplateMark to enable dynamic and functional templates. Stay tuned for a deep dive into the data modeling aspect of Accord Project templates!
+```
+namespace hello@1.0.0
+@template
+concept HelloWorld {
+ o String name
+}
+```
\ No newline at end of file
diff --git a/src/content/module2.md b/src/content/module2.md
index 212d7a9..1214212 100644
--- a/src/content/module2.md
+++ b/src/content/module2.md
@@ -1,78 +1,9 @@
-# Module 2: Understanding the Template Model
+# Module 2
-In this module, we'll explore the **Template Model**, a crucial component of the Accord Project template system. The Template Model defines the data structure that supports the natural language text in your templates, providing a bridge between the text and the executable logic. Understanding the Template Model will help you grasp how data is categorized and utilized within Accord Project templates.
+## _Draft a TemplateMark template._
-## What is the Template Model?
+Next define the [TemplateMark](https://github.com/accordproject/markdown-transform?tab=readme-ov-file#templatemark-dom) for the template in the TemplateMark Editor. In this case it is the plain-text world `"Hello"` followed by a space, then the variable `message` followed by `"."`.
-Unlike standard document templates (such as those created in Word or PDF), Accord Project templates use a _Template Model_ to associate a structured data model with the natural language text. This model categorizes and defines the types of different components used in the template, enabling the computer to understand and process the information effectively.
-
-![Template Model](/public/assets/content/template_model.png)
-
-The Template Model categorizes variables into different types—such as numbers, monetary amounts, dates, or references to organizations—allowing the computer to interpret the template's data correctly.
-
-## Example of a Template Model
-
-Let's examine a Template Model for a **Service Agreement** clause:
-
-```js
-/* The template model */
-namespace service@1.0.0
-
-concept Address {
- o String line1
- o String city
- o String state
- o String country
-}
-
-concept Service {
- o String serviceName
- o String description
- o DateTime serviceDate
-}
-
-concept PaymentDetails {
- o MonetaryAmount amount
- o DateTime dueDate
-}
-
-@template
-concept ServiceAgreementData {
- o String clientName
- o Address clientAddress
- o Service[] services
- o PaymentDetails payment
-}
```
-
-In this model:
-
-- **`Address`** defines the structure for addresses, including line1, city, state, and country.
-- **`Service`** specifies the details of the services being provided, including the service name, description, and service date.
-- **`PaymentDetails`** outlines the payment information, including the amount and due date.
-- **`ServiceAgreementData`** is the main data concept used in the template, incorporating `clientName`, `clientAddress`, a list of `services`, and `payment`.
-
-### Key Components
-
-- **Concepts**: Define data structures and types used in the template. For instance, `Address` and `Service` are concepts that model different types of data.
-- **Attributes**: Specify the type of data each concept holds, such as `String`, `Integer`, `DateTime`, or `MonetaryAmount`.
-- **Namespaces**: Organize and manage different versions of the models, such as `service@1.0.0`.
-
-## Concerto
-
-The Template Model is written in **Concerto**, a language used to define data models in Accord Project templates. Concerto supports a range of modeling capabilities, including:
-
-- Primitive types (e.g., numbers, dates)
-- Nested and optional data structures
-- Enumerations and relationships
-- Object-oriented style inheritance
-
-_More information about Concerto can be found in the [Concerto Model](https://concerto.accordproject.org/docs/intro) section of this documentation._
-
-## Summary
-
-In this module, we've introduced the Template Model, showcasing its role in defining and structuring the data used in Accord Project templates. By understanding the Template Model, you can create more sophisticated and functional templates that integrate seamlessly with TemplateMark text.
-
-## What's Next?
-
-In the next module, we'll dive into **Template Logic**—the business rules and logic that govern how templates are processed and executed. You'll learn how to implement and test the logic that drives template functionality, enhancing your ability to create dynamic and interactive templates. Stay tuned for an in-depth exploration of Template Logic!
+Hello {{message}}.
+```
\ No newline at end of file
diff --git a/src/content/module3.md b/src/content/module3.md
index 7cde2d8..d4b7713 100644
--- a/src/content/module3.md
+++ b/src/content/module3.md
@@ -1,78 +1,14 @@
-# Module 3: Exploring Template Logic
+# Module 3
-In this module, we will dive into **Template Logic**, the component that makes Accord Project templates not just machine-readable but also machine-executable. Template Logic allows you to embed rules and functions within your templates, enabling dynamic calculations and automated processing of contract data.
+## _Generate data(JSON) for the template_
-## What is Template Logic?
+Generate some data to the TemplateMark template conforming to the shape defined by the Concerto Model.
-While the Template Text and Template Model ensure that your templates are well-structured and machine-readable, Template Logic adds the capability to perform computations and enforce rules based on the data. This makes your templates not only executable but also interactive and adaptive to various scenarios.
+Define an instance of the `helloworld@1.0.0.TemplateData` data model. In this case setting the value of the `message` property to the string "World".
-![Template Logic](/public/assets/content/template_logic.png)
-
-### Logic During Drafting
-
-Template Logic can be embedded directly within the template text to perform real-time calculations or transformations based on the provided data. For example, consider a **Service Cost Estimate** template that calculates the total cost of services based on hourly rates and hours worked:
-
-```js
-## Service Cost Estimate
-
-This document provides an estimate for the services requested by {{clientName}}.
-
-### Service Details
-
-- Service: {{serviceName}}
-- Hours Worked: {{hoursWorked}}
-- Hourly Rate: {{hourlyRate as "0,0.00"}}
-- Estimated Total Cost: {{% calculateTotalCost(hoursWorked, hourlyRate) %}}
-
-Thank you for considering our services.
-```
-
-In this example, `calculateTotalCost` is a function that computes the total cost based on the hours worked and the hourly rate:
-
-```js
-define function calculateTotalCost(hoursWorked: Integer, hourlyRate: Double) : Double {
- return hoursWorked * hourlyRate;
-}
```
-
-Here, `calculateTotalCost` takes `hoursWorked` and `hourlyRate` as inputs and returns the total cost. The function uses simple arithmetic operations to compute the result.
-
-### Logic After Signature
-
-Template Logic can also be applied to enforce rules or conditions after a contract has been signed. For instance, imagine a **Warranty Agreement** that needs to verify if a warranty claim is valid based on the date of purchase and the claim submission date:
-
-```js
-contract WarrantyAgreement over WarrantyAgreementModel {
- clause validateClaim(request : WarrantyClaimRequest) : ClaimValidationResponse {
-
- let claimDate = request.claimDate;
- let purchaseDate = contract.purchaseDate;
- let warrantyPeriod = Duration{ amount: 1, unit: ~org.accordproject.time.TemporalUnit.years };
-
- enforce isAfter(claimDate, purchaseDate) else
- throw ErgoErrorResponse{ message : "Claim date is before the purchase date." }
- ;
-
- let isValid =
- if isBefore(claimDate, addDuration(purchaseDate, warrantyPeriod))
- then VALID_CLAIM
- else EXPIRED_CLAIM
- ;
- return ClaimValidationResponse{
- status : isValid,
- client : contract.client,
- warrantyDetails : contract.warrantyDetails
- }
- }
-}
-```
-
-This logic ensures that a warranty claim is only accepted if it falls within the warranty period. It checks if the claim date is after the purchase date and within the warranty duration, returning a validation response accordingly.
-
-## Summary
-
-In this module, we've explored Template Logic and how it enables your templates to perform computations and enforce rules dynamically. By embedding logic into your templates, you can create more interactive and responsive documents that adapt to different data inputs and scenarios.
-
-## Share Your Work
-
-Once you’ve finished editing or using a template, you can easily share your work with others. Simply click the **Share** button to generate a shareable link. This allows you to collaborate with others or distribute your completed templates efficiently.
+const data = {
+ $class: 'helloworld@1.0.0.TemplateData',
+ message: 'World',
+};
+```
\ No newline at end of file
diff --git a/src/editors/editorsContainer/AgreementData.tsx b/src/editors/editorsContainer/AgreementData.tsx
index dffb0a0..ce46e81 100644
--- a/src/editors/editorsContainer/AgreementData.tsx
+++ b/src/editors/editorsContainer/AgreementData.tsx
@@ -1,11 +1,13 @@
import JSONEditor from "../JSONEditor";
import useAppStore from "../../store/store";
-import { useCallback } from 'react';
+import { useCallback } from "react";
import { debounce } from "ts-debounce";
function AgreementData() {
const editorAgreementData = useAppStore((state) => state.editorAgreementData);
- const setEditorAgreementData = useAppStore((state) => state.setEditorAgreementData);
+ const setEditorAgreementData = useAppStore(
+ (state) => state.setEditorAgreementData
+ );
const setData = useAppStore((state) => state.setData);
const debouncedSetData = useCallback(
diff --git a/src/editors/editorsContainer/TemplateMarkdown.tsx b/src/editors/editorsContainer/TemplateMarkdown.tsx
index 8650499..e8776f3 100644
--- a/src/editors/editorsContainer/TemplateMarkdown.tsx
+++ b/src/editors/editorsContainer/TemplateMarkdown.tsx
@@ -1,6 +1,6 @@
import MarkdownEditor from "../MarkdownEditor";
import useAppStore from "../../store/store";
-import { useCallback } from 'react';
+import { useCallback } from "react";
import { debounce } from "ts-debounce";
function TemplateMarkdown() {
diff --git a/src/editors/editorsContainer/TemplateModel.tsx b/src/editors/editorsContainer/TemplateModel.tsx
index c1ab319..757f38b 100644
--- a/src/editors/editorsContainer/TemplateModel.tsx
+++ b/src/editors/editorsContainer/TemplateModel.tsx
@@ -1,6 +1,6 @@
import ConcertoEditor from "../ConcertoEditor";
import useAppStore from "../../store/store";
-import { useCallback } from 'react';
+import { useCallback } from "react";
import { debounce } from "ts-debounce";
function TemplateModel() {
diff --git a/src/pages/LearnNow.tsx b/src/pages/LearnNow.tsx
new file mode 100644
index 0000000..26f1b90
--- /dev/null
+++ b/src/pages/LearnNow.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+import { Outlet } from "react-router-dom";
+import Sidebar from "../components/Sidebar";
+import { LearnNowContainer } from "../styles/pages/LearnNow";
+import { steps } from "../constants/learningSteps/steps";
+
+const LearnNow: React.FC = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default LearnNow;
diff --git a/src/styles/components/Content.ts b/src/styles/components/Content.ts
new file mode 100644
index 0000000..a4647ef
--- /dev/null
+++ b/src/styles/components/Content.ts
@@ -0,0 +1,124 @@
+import styled from "styled-components";
+
+export const ContentContainer = styled.div`
+ padding: 20px;
+ max-width: 900px;
+ width: 100%;
+ margin: 0 auto;
+ background: #ffffff;
+ border-radius: 8px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ box-sizing: border-box;
+
+ h1,
+ h2,
+ h3,
+ h4,
+ h5,
+ h6 {
+ margin-top: 1em;
+ margin-bottom: 0.5em;
+ color: #333;
+ }
+
+ p {
+ line-height: 1.6;
+ margin-bottom: 1em;
+ }
+
+ a {
+ color: #19c6c7;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ ul,
+ ol {
+ margin: 1em 0;
+
+ li {
+ margin-bottom: 0.5em;
+ }
+ }
+
+ img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+ margin: 0 auto;
+ }
+
+ .image-container {
+ display: flex;
+ justify-content: center;
+ }
+
+ @media (max-width: 1200px) {
+ max-width: 90%;
+ }
+
+ @media (max-width: 768px) {
+ padding: 15px;
+ }
+`;
+
+export const NavigationButtons = styled.div`
+ margin-top: 60px;
+ margin-bottom: 20px;
+ display: flex;
+ justify-content: space-between;
+
+ @media (max-width: 768px) {
+ flex-direction: column;
+ align-items: stretch;
+
+ button {
+ margin-bottom: 10px;
+ }
+ }
+`;
+
+export const NavigationButton = styled.button`
+ padding: 10px 20px;
+ border: 2px solid #19c6c7;
+ border-radius: 4px;
+ background-color: white;
+ color: #1b2540;
+ cursor: pointer;
+ font-weight: 600;
+ font-size: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:disabled {
+ background-color: #e0e0e0;
+ color: #9e9e9e;
+ cursor: not-allowed;
+ border-color: #e0e0e0;
+ }
+
+ &:hover:not(:disabled) {
+ background-color: #19c6c7;
+ color: white;
+ text-decoration: underline;
+ }
+
+ svg {
+ margin-right: 8px;
+ }
+
+ &:nth-child(1) svg {
+ margin-right: 8px;
+ }
+
+ &:nth-child(2) {
+ svg {
+ margin-right: 0;
+ margin-left: 8px;
+ }
+ }
+`;
diff --git a/src/styles/components/Sidebar.ts b/src/styles/components/Sidebar.ts
new file mode 100644
index 0000000..0c59dcc
--- /dev/null
+++ b/src/styles/components/Sidebar.ts
@@ -0,0 +1,101 @@
+import styled from "styled-components";
+import { NavLink } from "react-router-dom";
+
+export const SidebarContainer = styled.div`
+ width: 260px;
+ background-color: #f5f5f5;
+ padding: 1rem;
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
+ border-radius: 4px;
+ position: relative;
+ overflow-y: auto;
+
+ @media (max-width: 768px) {
+ width: 100%;
+ height: auto;
+ position: static;
+ }
+`;
+
+export const SidebarTitle = styled.h2`
+ font-size: 1.2rem;
+ font-weight: 500;
+ margin-bottom: 1rem;
+ color: #333;
+`;
+
+export const SidebarList = styled.ul`
+ list-style: none;
+ padding: 0;
+ margin: 0;
+`;
+
+export const SidebarListItem = styled.li`
+ margin-bottom: 0.5rem;
+`;
+
+export const SidebarLink = styled(NavLink)`
+ text-decoration: none;
+ display: block;
+ padding: 0.8rem 1rem;
+ border-radius: 4px;
+ font-size: 0.95rem;
+ font-weight: 600;
+ color: #717171;
+ transition: background-color 0.3s, color 0.3s;
+
+ &.active {
+ background-color: #e0e0e0;
+ color: #1b2540;
+ font-weight: 600;
+ border-left: 2.5px solid #19c6c7;
+ }
+
+ &:hover {
+ background-color: #e0e0e0;
+ color: #050c40;
+ text-decoration: underline;
+ }
+`;
+
+export const HelperBox = styled.div`
+ margin-top: 1rem;
+ padding: 0.8rem 1rem;
+ background-color: #e6f7ff;
+ border-radius: 4px;
+ font-size: 0.9rem;
+
+ @media (max-width: 768px) {
+ display: none;
+ }
+`;
+
+export const HelperIcon = styled.div`
+ margin-right: 0.5rem;
+ font-size: 1.2rem;
+ color: #19c6c7;
+`;
+
+export const HelperText = styled.div`
+ flex: 1;
+ color: #333;
+
+ a {
+ color: #19c6c7;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ & > a {
+ margin-left: 0.25rem;
+ }
+`;
+
+export const DividerLine = styled.div`
+ height: 1px;
+ background-color: #ddd;
+ margin: 7rem 0 1rem 0;
+`;
diff --git a/src/styles/pages/LearnNow.ts b/src/styles/pages/LearnNow.ts
new file mode 100644
index 0000000..5939f9a
--- /dev/null
+++ b/src/styles/pages/LearnNow.ts
@@ -0,0 +1,35 @@
+import styled from "styled-components";
+
+export const LearnNowContainer = styled.div`
+ display: flex;
+`;
+
+export const SidebarContainer = styled.div`
+ width: 250px;
+ background-color: #f4f4f4;
+ padding: 20px;
+ border-right: 1px solid #ddd;
+
+ h2 {
+ margin-top: 0;
+ }
+
+ ul {
+ list-style: none;
+ padding: 0;
+ }
+
+ li {
+ margin-bottom: 10px;
+ }
+
+ a {
+ text-decoration: none;
+ color: #333;
+ }
+`;
+
+export const ContentContainer = styled.div`
+ flex: 1;
+ padding: 20px;
+`;
diff --git a/src/tests/components/Navbar.test.tsx b/src/tests/components/Navbar.test.tsx
index b6ba37e..0c23689 100644
--- a/src/tests/components/Navbar.test.tsx
+++ b/src/tests/components/Navbar.test.tsx
@@ -1,10 +1,19 @@
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import Navbar from "../../components/Navbar";
+import { MemoryRouter } from "react-router-dom";
+
+const renderNavbar = () => {
+ render(
+
+ {}} />
+
+ );
+};
describe("Navbar", () => {
it("renders logo and title on small screens", () => {
- render(
{}} />);
+ renderNavbar();
const logoImage = screen.getByRole("img", { name: /Template Playground/i });
expect(logoImage).toBeInTheDocument();
@@ -14,14 +23,14 @@ describe("Navbar", () => {
});
it("renders Github link on all screens", () => {
- render( {}} />);
+ renderNavbar();
const githubLink = screen.getByRole("link", { name: /Github/i });
expect(githubLink).toBeInTheDocument();
});
it("shows hover effect on menu items", () => {
- render( {}} />);
+ renderNavbar();
const homeMenuItem = screen
.getByText(/Template Playground/i)
diff --git a/src/utils/fetchContent.ts b/src/utils/fetchContent.ts
new file mode 100644
index 0000000..87bb8ed
--- /dev/null
+++ b/src/utils/fetchContent.ts
@@ -0,0 +1,9 @@
+const fetchContent = async (fileName: string): Promise => {
+ const response = await fetch(`https://raw.githubusercontent.com/accordproject/template-playground/main/src/content/${fileName}`);
+ if (!response.ok) {
+ throw new Error(`Content not found: ${fileName}`);
+ }
+ return response.text();
+};
+
+export default fetchContent;
diff --git a/vite.config.ts b/vite.config.ts
index 9beaaa8..966d85f 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -6,6 +6,9 @@ import nodePolyfills from "vite-plugin-node-stdlib-browser";
// https://vitejs.dev/config/
const viteConfig = defineViteConfig({
plugins: [nodePolyfills(), react()],
+ optimizeDeps: {
+ include: ["immer"],
+ },
});
// https://vitest.dev/config/