diff --git a/app/blog/[slug]/layout.css b/app/blog/[slug]/layout.css index 247798a..72dcab2 100644 --- a/app/blog/[slug]/layout.css +++ b/app/blog/[slug]/layout.css @@ -30,4 +30,8 @@ .markdown-content p { margin-bottom: 1rem; +} + +html { + scroll-behavior: smooth; } \ No newline at end of file diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx index 7c10261..db45442 100644 --- a/app/blog/[slug]/page.tsx +++ b/app/blog/[slug]/page.tsx @@ -1,13 +1,13 @@ import fs from 'fs' -import { Source_Serif_4 } from 'next/font/google' +import { Inter } from 'next/font/google' import { getPostData } from '@/lib/posts' import formatDate from '@/lib/format' import Link from 'next/link' import path from 'path' import { DINish } from '@/app/fonts' +import PageNavigator from '@/components/pageNavigator' -const sourceSerifPro = Source_Serif_4({ subsets: ['latin'], weight: ['400', '500', '600'] }) - +const inter = Inter({ subsets: ['latin'], weight: ['400', '500', '600'] }) type Params = { slug: string } @@ -80,25 +80,28 @@ export default async function Post({ params }: Props) { const { date, time, author } = postData; return ( -
+
{/* Post Title */} -
+
Back to posts -

{postData.title}

+

{postData.title}

{/* Post Content */}
+ + {/* Page Navigator */} +
) } diff --git a/app/blog/page.tsx b/app/blog/page.tsx index fa50b0d..45f5652 100644 --- a/app/blog/page.tsx +++ b/app/blog/page.tsx @@ -1,5 +1,5 @@ import { getSortedPostsData } from '@/lib/posts' -import { Source_Serif_4 } from 'next/font/google' +import { Source_Serif_4, Inter } from 'next/font/google' import Link from 'next/link' import { DateTime } from 'luxon' import Image from 'next/image' @@ -7,7 +7,7 @@ import { DINish } from '../fonts' const sourceSerifPro = Source_Serif_4({ subsets: ['latin'], weight: ['400', '500', '600'] }) - +const inter = Inter({ subsets: ['latin'], weight: ['400', '500', '600'] }) type AllPostsData = { date: string @@ -54,7 +54,7 @@ export default function Blog() {

{title}

-

+

{description}

diff --git a/app/hero_v2.tsx b/app/hero_v2.tsx index 86c676b..4513158 100644 --- a/app/hero_v2.tsx +++ b/app/hero_v2.tsx @@ -1,7 +1,7 @@ "use client"; // @ts-ignore -import riveWASMResource from '@rive-app/canvas/rive.wasm'; +// import riveWASMResource from '@rive-app/canvas/rive.wasm'; import Rive, { useRive, Layout, Fit, Alignment, RuntimeLoader } from "@rive-app/react-canvas"; import { useEffect, useLayoutEffect, useRef, useState } from "react"; import { DINish } from './fonts'; @@ -9,7 +9,7 @@ import { createPortal } from 'react-dom'; import Modal from '@/components/modal'; import { CSSTransition } from 'react-transition-group'; -RuntimeLoader.setWasmUrl(riveWASMResource); +// RuntimeLoader.setWasmUrl(riveWASMResource); const Hero = () => { const [artboard, setArtboard] = useState('Artboard_sm'); @@ -26,7 +26,6 @@ const Hero = () => { const ro = new ResizeObserver((entries) => { const { width, height } = entries[0].contentRect; if (width >= 1024) { - console.log('update') setArtboard('Artboard') setStateMachine('hero_sme') } else { @@ -39,8 +38,6 @@ const Hero = () => { } }, []); - console.log('stateMachine', stateMachine) - return (

Finally, business metrics at your fingertips

diff --git a/app/interactive.tsx b/app/interactive.tsx index a7a5363..660a710 100644 --- a/app/interactive.tsx +++ b/app/interactive.tsx @@ -29,31 +29,6 @@ export default function Interactive() { }, []); return ( - //
- //
- //
- //
- //
- //
- // - // - // - //

Interactive analytics

- //
- //

- // Quickly slice and dice your data to uncover key business insights. - //

- //

- // EdgeSet supports a variety of visualizations, offering flexibility in how data can be displayed. - //

- //
- //
- //
- //
- //
- //
- //
diff --git a/app/layout.tsx b/app/layout.tsx index a663220..6722290 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,6 @@ import type { Metadata } from 'next' import { Inter, Work_Sans } from 'next/font/google' +import riveWASMResource from '@rive-app/canvas/rive.wasm'; import './globals.css' const inter = Inter({ subsets: ['latin'], weight: ['400', '500', '600'] }) @@ -29,6 +30,9 @@ export default function RootLayout({ }) { return ( + + + {children} diff --git a/app/page.tsx b/app/page.tsx index 62894c1..ca04ef9 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,10 +1,6 @@ import Navbar from '@/components/navbar'; import Footer from '@/components/footer'; -// import Radar from '../components/radar'; -// import SinglePane from './singlePane'; -// import ThreeV from './threeV'; -// import Hero from './hero'; import Platform from './platform'; import Summary from './summary'; import Security from './security'; diff --git a/app/security.tsx b/app/security.tsx index 2393f65..6e907c6 100644 --- a/app/security.tsx +++ b/app/security.tsx @@ -1,14 +1,11 @@ "use client"; +import useIsMobile from "@/hooks/useIsMobile"; import { DINish } from "./fonts"; -// @ts-ignore -import riveWASMResource from '@rive-app/canvas/rive.wasm'; import Rive, { useRive, Layout, Fit, Alignment, Rive as RType, RuntimeLoader } from "@rive-app/react-canvas"; import { useEffect, useRef, useState } from "react"; -RuntimeLoader.setWasmUrl(riveWASMResource); - export default function App() { const granularRef = useRef(null); const internalRef = useRef(null); @@ -16,183 +13,53 @@ export default function App() { const granularInlineRef = useRef(null); const internalInlineRef = useRef(null); const encryptionInlineRef = useRef(null); - const riveRef = useRef(null); - const canvasRef = useRef(null); const containerRef = useRef(null); const [showHalo, toggleHalo] = useState(false); + const isMobile = useIsMobile(); let { RiveComponent, rive } = useRive({ - // Load a local riv `clean_the_car.riv` or upload your own! src: "security.riv", artboard: "Granular_Artboard", - // Be sure to specify the correct state machine (or animation) name stateMachines: ["Granular", "Internal", "Encryption"], - onLoop: (event) => { - console.log("Looping", event); - }, - onLoad: (event) => { - console.log("Loaded", event); - // rive?.play(); - }, - onPlay: (event) => { - console.log("Playing", event); - }, - // onAdvance: (event) => { console.log("Advance", event); }, - onStateChange: (event) => { - console.log("State Change", event); - - if (Array.isArray(event.data) && event.data.includes("exit")) { - console.log('stop--') - // setIsPlaying(false); - // riveRef.current?.reset(); - // riveRef.current?.play("Granular"); - // globalRive?.stopRendering(); - // rive. - } - }, - onStop: (event) => { console.log("Stopped", event, riveRef.current); }, - // onAdvance: (event) => { console.log("Advanced", event); }, - onPause: (event) => { console.log("Paused", event, rive); }, - // This is optional.Provides additional layout control. layout: new Layout({ fit: Fit.Contain, - // fit: Fit.Cover, // Change to: rive.Fit.Contain, or Cover alignment: Alignment.TopCenter, - // maxX: 500, - // minY: 500 }), autoplay: true, }); - let { RiveComponent: GranularRiveComponent, rive: granularRive } = useRive({ - // Load a local riv `clean_the_car.riv` or upload your own! + let { RiveComponent: GranularRiveComponent, rive: granularRive } = isMobile ? useRive({ src: "security.riv", artboard: "Granular_Artboard", - // Be sure to specify the correct state machine (or animation) name stateMachines: ["Granular"], - onLoop: (event) => { - console.log("Looping", event); - }, - onLoad: (event) => { - console.log("Loaded", event); - // rive?.play(); - }, - onPlay: (event) => { - console.log("Playing", event); - }, - // onAdvance: (event) => { console.log("Advance", event); }, - onStateChange: (event) => { - console.log("State Change", event); - - if (Array.isArray(event.data) && event.data.includes("exit")) { - console.log('stop--') - // setIsPlaying(false); - // riveRef.current?.reset(); - // riveRef.current?.play("Granular"); - // globalRive?.stopRendering(); - // rive. - } - }, - onStop: (event) => { console.log("Stopped", event, riveRef.current); }, - // onAdvance: (event) => { console.log("Advanced", event); }, - onPause: (event) => { console.log("Paused", event, rive); }, - // This is optional.Provides additional layout control. + autoplay: true, layout: new Layout({ fit: Fit.FitWidth, - // fit: Fit.Cover, // Change to: rive.Fit.Contain, or Cover alignment: Alignment.TopCenter, - // maxX: 500, - // minY: 500 }), - autoplay: true, - }); - + }) : { RiveComponent: null, rive: null }; - let { RiveComponent: InternalRiveComponent, rive: internalRive } = useRive({ - // Load a local riv `clean_the_car.riv` or upload your own! + let { RiveComponent: InternalRiveComponent, rive: internalRive } = isMobile ? useRive({ src: "security.riv", artboard: "Internal_Artboard", - // Be sure to specify the correct state machine (or animation) name stateMachines: ["Internal"], - onLoop: (event) => { - console.log("Looping", event); - }, - onLoad: (event) => { - console.log("Loaded", event); - // rive?.play(); - }, - onPlay: (event) => { - console.log("Playing", event); - }, - // onAdvance: (event) => { console.log("Advance", event); }, - onStateChange: (event) => { - console.log("State Change", event); - - if (Array.isArray(event.data) && event.data.includes("exit")) { - console.log('stop--') - // setIsPlaying(false); - // riveRef.current?.reset(); - // riveRef.current?.play("Granular"); - // globalRive?.stopRendering(); - // rive. - } - }, - onStop: (event) => { console.log("Stopped", event, riveRef.current); }, - // onAdvance: (event) => { console.log("Advanced", event); }, - onPause: (event) => { console.log("Paused", event, rive); }, - // This is optional.Provides additional layout control. + autoplay: true, layout: new Layout({ fit: Fit.FitWidth, - // fit: Fit.Cover, // Change to: rive.Fit.Contain, or Cover alignment: Alignment.TopCenter, - // maxX: 500, - // minY: 500 }), - autoplay: true, - }); + }) : { RiveComponent: null, rive: null }; - let { RiveComponent: EncryptionRiveComponent, rive: encryptionRive } = useRive({ - // Load a local riv `clean_the_car.riv` or upload your own! + let { RiveComponent: EncryptionRiveComponent, rive: encryptionRive } = isMobile ? useRive({ src: "security.riv", artboard: "Encryption_Artboard", - // Be sure to specify the correct state machine (or animation) name stateMachines: ["Encryption"], - onLoop: (event) => { - console.log("Looping", event); - }, - onLoad: (event) => { - console.log("Loaded", event); - // rive?.play(); - }, - onPlay: (event) => { - console.log("Playing", event); - }, - // onAdvance: (event) => { console.log("Advance", event); }, - onStateChange: (event) => { - console.log("State Change", event); - - if (Array.isArray(event.data) && event.data.includes("exit")) { - console.log('stop--') - // setIsPlaying(false); - // riveRef.current?.reset(); - // riveRef.current?.play("Granular"); - // globalRive?.stopRendering(); - // rive. - } - }, - onStop: (event) => { console.log("Stopped", event, riveRef.current); }, - // onAdvance: (event) => { console.log("Advanced", event); }, - onPause: (event) => { console.log("Paused", event, rive); }, - // This is optional.Provides additional layout control. layout: new Layout({ fit: Fit.FitWidth, - // fit: Fit.Cover, // Change to: rive.Fit.Contain, or Cover alignment: Alignment.TopCenter, - // maxX: 500, - // minY: 500 }), autoplay: true, - }); + }) : { RiveComponent: null, rive: null }; useEffect(() => { if (!granularRef.current || !internalRef.current || !encryptionRef.current || !containerRef.current) { @@ -210,8 +77,6 @@ export default function App() { if (entry.isIntersecting) { console.log('entry', entry.target.id); toggleHalo(true); - // console.log('Log event and unobserve: granular'); - // observer.unobserve(entry.target); if (entry.target.id === 'encryption' && rive) { rive.reset({ artboard: "Encryption_Artboard" }); try { @@ -237,12 +102,8 @@ export default function App() { console.log('Error playing rive') } } - // if (riveRef.current) { - // riveRef.current?.play("Granular"); - // } } else { console.log('exit', entry.target.id); - // toggleHalo(false); } }); }, options); @@ -250,43 +111,7 @@ export default function App() { observer.observe(granularRef.current); observer.observe(internalRef.current); observer.observe(encryptionRef.current); - // observer.observe(containerRef.current); - - let containerObserver = new IntersectionObserver((entries) => { - entries.map((entry) => { - if (entry.isIntersecting) { - console.log('container enter') - toggleHalo(true); - } else { - console.log('container exit') - toggleHalo(false); - } - }); - }, { - threshold: 0.2 - }); - - containerObserver.observe(containerRef.current) - }); - - // useEffect(() => { - // if (!containerRef.current) { - // return; - // } - // let containerObserver = new IntersectionObserver((entries) => { - // entries.map((entry) => { - // if (entry.isIntersecting) { - // document.dispatchEvent(new Event('security-enter')); - // } else { - // document.dispatchEvent(new Event('security-exit')); - // } - // }); - // }, { - // threshold: 0.2 - // }); - - // containerObserver.observe(containerRef.current) - // }, []); + }, [rive, internalRive, encryptionRive]); useEffect(() => { if (granularInlineRef.current && granularRive) { @@ -460,7 +285,7 @@ export default function App() {

To counter Insider Threats

- + {GranularRiveComponent ? : null}
  • @@ -475,7 +300,7 @@ export default function App() {

    To counter Vendor Threats

    - + {InternalRiveComponent ? : null}

    Your data is not sent to us.

    @@ -501,7 +326,7 @@ export default function App() {

    To counter External Threats

    - + {EncryptionRiveComponent ? : null}

    EdgeSet was designed with hardened security from the ground up.

      diff --git a/components/navbar.tsx b/components/navbar.tsx index 31d7e9c..24164e0 100644 --- a/components/navbar.tsx +++ b/components/navbar.tsx @@ -1,7 +1,6 @@ 'use client' import { useEffect, useState } from 'react'; -import Link from 'next/link'; import Menu from './mobileMenu'; import { DINish } from '@/app/fonts'; diff --git a/components/pageNavigator.tsx b/components/pageNavigator.tsx new file mode 100644 index 0000000..066b39d --- /dev/null +++ b/components/pageNavigator.tsx @@ -0,0 +1,70 @@ +'use client' + +import React, { useEffect, useState } from 'react' + +const PageNavigator: React.FC = () => { + const [headings, setHeadings] = useState([]) + const [activeHeading, setActiveHeading] = useState(null) + + useEffect(() => { + const headingElements = Array.from(document.querySelectorAll('h2')) + setHeadings(headingElements as HTMLHeadingElement[]) + + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveHeading(entry.target.id) + } + }) + }, + { rootMargin: '-100px 0px -66% 0px' } + ) + + headingElements.forEach((heading) => observer.observe(heading)) + + // detect scroll to bottom + window.addEventListener('scroll', () => { + if (window.scrollY + window.innerHeight >= document.body.scrollHeight) { + setActiveHeading(headingElements[headingElements.length - 1].id) + } + }); + + // on page load, set active heading to the first heading + setActiveHeading(headingElements[0].id); + + return () => observer.disconnect() + }, []) + + return ( + + ) +} + +export default PageNavigator \ No newline at end of file diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..43b469a --- /dev/null +++ b/global.d.ts @@ -0,0 +1,4 @@ +declare module '*.wasm' { + const content: string; + export default content; +} \ No newline at end of file diff --git a/hooks/useIsMobile.tsx b/hooks/useIsMobile.tsx new file mode 100644 index 0000000..147b280 --- /dev/null +++ b/hooks/useIsMobile.tsx @@ -0,0 +1,20 @@ +import { useState, useEffect } from 'react'; + +const useIsMobile = (breakpoint: number = 1024) => { + const [isMobile, setIsMobile] = useState(false); + + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth < breakpoint); + }; + + checkIsMobile(); + window.addEventListener('resize', checkIsMobile); + + return () => window.removeEventListener('resize', checkIsMobile); + }, [breakpoint]); + + return isMobile; +}; + +export default useIsMobile; \ No newline at end of file diff --git a/lib/posts.ts b/lib/posts.ts index 3980097..70dcc2a 100644 --- a/lib/posts.ts +++ b/lib/posts.ts @@ -16,10 +16,13 @@ const images = ['better.png', 'lock.png', 'peer2peer.png', 'etl.png'] // Import 'gray-matter', library for parsing the metadata in each markdown file import matter from 'gray-matter' -// Import 'remark', library for rendering markdown -import { remark } from 'remark' -import html from 'remark-html' import remarkGfm from 'remark-gfm' +import rehypeSlug from 'rehype-slug' +import { unified } from 'unified' +import remarkRehype from 'remark-rehype' +import remarkParse from 'remark-parse' +import rehypeStringify from 'rehype-stringify' + // -------------------------------- // GET THE PATH OF THE POSTS FOLDER @@ -126,9 +129,12 @@ export async function getPostData(id: string, dir: string = postsDirectory) { const matterResult = matter(fileContents); // Use remark to convert markdown into HTML string - const processedContent = await remark() - .use(html, { sanitize: false }) + const processedContent = await unified() + .use(remarkParse) .use(remarkGfm) + .use(remarkRehype) + .use(rehypeSlug) // Add this line' + .use(rehypeStringify) .process(matterResult.content); const contentHtml = processedContent.toString(); diff --git a/package-lock.json b/package-lock.json index 2063b89..0fe24aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,10 +17,16 @@ "react": "^18", "react-dom": "^18", "react-transition-group": "^4.4.5", + "rehype-slug": "^6.0.0", + "rehype-stringify": "^10.0.1", "remark": "^15.0.1", "remark-gfm": "^4.0.0", "remark-html": "^16.0.1", - "tinacms": "^1.5.28" + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.1", + "tinacms": "^1.5.28", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0" }, "devDependencies": { "@tinacms/cli": "^1.5.39", @@ -10114,6 +10120,11 @@ "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" + }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -10406,6 +10417,18 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-heading-rank": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz", + "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-parse-selector": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", @@ -10497,6 +10520,18 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -18022,6 +18057,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz", + "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==", + "dependencies": { + "@types/hast": "^3.0.0", + "github-slugger": "^2.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", + "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/relay-runtime": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz", @@ -18329,6 +18394,22 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-stringify": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", @@ -20097,9 +20178,9 @@ "dev": true }, "node_modules/unified": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", - "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", diff --git a/package.json b/package.json index 019eb4f..3dcbd70 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,16 @@ "react": "^18", "react-dom": "^18", "react-transition-group": "^4.4.5", + "rehype-slug": "^6.0.0", + "rehype-stringify": "^10.0.1", "remark": "^15.0.1", "remark-gfm": "^4.0.0", "remark-html": "^16.0.1", - "tinacms": "^1.5.28" + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.1", + "tinacms": "^1.5.28", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0" }, "devDependencies": { "@tinacms/cli": "^1.5.39", diff --git a/posts/how-we-created-our-own-crm-with-just-edgeset-and-google-sheets.md b/posts/how-we-created-our-own-crm-with-just-edgeset-and-google-sheets.md index 043c134..c398db2 100644 --- a/posts/how-we-created-our-own-crm-with-just-edgeset-and-google-sheets.md +++ b/posts/how-we-created-our-own-crm-with-just-edgeset-and-google-sheets.md @@ -70,7 +70,7 @@ We were so overjoyed with the first iteration that we continuously added things It makes for easier communication internally within our startup during our weekly standups, and also for external communications by just taking screenshots. -## Advantages: +## Advantages * Use your google sheets and Excels as if they were SQL databases, not only for this, **but also to digitally transform your whole business**.  * Fast to setup (less than one hour) and real-time display of the sales pipeline. @@ -84,11 +84,11 @@ It makes for easier communication internally within our startup during our weekl * Security and privacy * On-premise or desktop version: no one but you (not even us) sees your valuable data. -

      Disadvantage:

      +## Disadvantage You may get criticised or laughed at  for not having a proper CRM, but hey, a simple exercise routine of 10 minutes a day that you can keep to, beats a fancy personal-trainer crafted routine that you end up lapsing on. And, once you show your actual sales numbers, criticism melts away, and morphs into respect.  -## Conclusion: +## Conclusion One may think that buying a sales CRM tool or ERP system is a part of your business "growing up", but from our direct experience with sales organizations with 100+ salespeople, people naturally fall back to using spreadsheets, exporting data in CSVs, and doing analysis from there. EdgeSet allows your sales numbers to actually speak for themselves, not some fancy CRM software.  diff --git a/tsconfig.json b/tsconfig.json index 7efe607..fb3d9c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -33,9 +33,10 @@ "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", - "out/types/**/*.ts" + "out/types/**/*.ts", + "global.d.ts" ], "exclude": [ "node_modules" ] -} +} \ No newline at end of file