Skip to content

Commit

Permalink
fix: improve workshop
Browse files Browse the repository at this point in the history
  • Loading branch information
elribonazo committed Nov 5, 2024
1 parent b2cfeaa commit 0eb88b2
Show file tree
Hide file tree
Showing 9 changed files with 639 additions and 244 deletions.
257 changes: 257 additions & 0 deletions demos/next/package-lock.json

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions demos/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@
"lint": "next lint"
},
"dependencies": {
"@heroicons/react": "^1.0.6",
"@hyperledger/identus-edge-agent-sdk": "../..",
"@noble/hashes": "^1.3.3",
"@pluto-encrypted/indexdb": "^1.12.2",
"@reduxjs/toolkit": "^1.9.5",
"crypto": "^1.0.1",
"fs": "^0.0.1-security",
"@noble/hashes": "^1.3.3",
"jose": "^5.2.0",
"jotai": "^2.6.1",
"next": "14.0.4",
"path": "^0.12.7",
"react": "^18",
"react-dom": "^18",
"react-redux": "^7.2.6",
"react-syntax-highlighter": "^15.6.1",
"redux": "^4.1.2",
"redux-thunk": "^2.4.0",
"@reduxjs/toolkit": "^1.9.5"
"redux-thunk": "^2.4.0"
},
"devDependencies": {
"@types/node": "^20",
Expand All @@ -36,4 +38,4 @@
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
}
178 changes: 178 additions & 0 deletions demos/next/src/components/ApiCallComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { ApiCall } from "@/reducers/app";
import dynamic from "next/dynamic";
import { useState, useEffect } from "react";
import { Store } from "redux";

const CodeComponent = dynamic(() => import('@/components/CodeEditor').then((e) => e.CodeComponent), {
ssr: false,
});

export const ApiCallComponent: React.FC<{
content: ApiCall,
store: Store,
onResponse: (response: any) => void
}> = (props) => {
const [response, setResponse] = useState<any>(null);
const [isPopupOpen, setIsPopupOpen] = useState(false);
const [isCurlPopupOpen, setIsCurlPopupOpen] = useState(false);
const [requestStatus, setRequestStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');

const { content } = props;

const url = content.endpoint(props.store);
const body = content.requestBody(props.store);

const curlCommand = content.curlCommand(
url,
content.method,
body
);

useEffect(() => {
const handleKeyDown = (e) => {
if (e.key === "Escape") {
setIsPopupOpen(false);
setIsCurlPopupOpen(false);
}
};

if (isPopupOpen || isCurlPopupOpen) {
document.addEventListener("keydown", handleKeyDown);
} else {
document.removeEventListener("keydown", handleKeyDown);
}

return () => {
document.removeEventListener("keydown", handleKeyDown);
};
}, [isPopupOpen, isCurlPopupOpen]);

const handleRunInBrowser = () => {
setRequestStatus('loading');
fetch(url, {
method: content.method,
headers: {
"Content-Type": "application/json",
},
body: content.method === "POST" ? JSON.stringify(body) : undefined,
})
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log("API response:", data);
setResponse(data);
if (props.onResponse) {
props.onResponse(data)
}
setRequestStatus('success');
})
.catch((error) => {
console.error("API call error:", error);
setRequestStatus('error');
});
};

const handleCopyCurl = () => {
setIsCurlPopupOpen(true);
};

let runButtonClasses =
"px-3 py-1 text-sm text-white rounded-lg transition duration-200 ";

switch (requestStatus) {
case 'idle':
runButtonClasses += "bg-gray-500 hover:bg-gray-400";
break;
case 'loading':
runButtonClasses += "bg-blue-500 hover:bg-blue-400";
break;
case 'success':
runButtonClasses += "bg-green-500 hover:bg-green-400";
break;
case 'error':
runButtonClasses += "bg-red-500 hover:bg-red-400";
break;
default:
runButtonClasses += "bg-gray-500 hover:bg-gray-400";
}

return (
<div className="my-4">
<div className="flex items-center mb-2">
<div className="flex-grow">
<p className="text-lg font-semibold text-gray-800">
{content.title}
</p>
<p className="text-xs font-semibold text-gray-800">
{content.description}
</p>
</div>
</div>
<div className="flex space-x-2">
<button
className={runButtonClasses}
onClick={handleRunInBrowser}
>
Run in Browser
</button>
<button
className="px-3 py-1 text-sm bg-blue-500 text-white rounded-lg hover:bg-blue-400 transition duration-200"
onClick={handleCopyCurl}
>
Show me cURL
</button>
{response && (
<button
className="px-3 py-1 text-sm bg-gray-500 text-white rounded-lg hover:bg-gray-400 transition duration-200"
onClick={() => setIsPopupOpen(true)}
>
Show me response
</button>
)}
</div>

{/* Response Modal */}
{isPopupOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white p-6 rounded-lg max-w-2xl mx-auto overflow-y-auto max-h-screen shadow-lg">
<h2 className="text-lg font-semibold mb-4 text-blue-700">API Response</h2>
<pre className="bg-gray-800 text-white p-4 rounded-lg shadow-md overflow-auto max-h-80 whitespace-pre-wrap">
<code>{JSON.stringify(response, null, 2)}</code>
</pre>
<div className="mt-4 flex justify-end">
<button
className="mt-4 px-3 py-1 text-sm bg-red-500 text-white rounded-lg hover:bg-red-400 transition duration-200"
onClick={() => setIsPopupOpen(false)}
>
Close
</button>
</div>
</div>
</div>
)}

{/* cURL Modal */}
{isCurlPopupOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<div className="bg-white p-6 rounded-lg max-w-2xl mx-auto overflow-y-auto max-h-screen shadow-lg">
<h2 className="text-lg font-semibold mb-4 text-blue-700">cURL Command</h2>
<CodeComponent content={{ language: 'bash', code: curlCommand }} />

<div className="mt-4 flex justify-end">
<button
className="px-3 py-1 text-sm bg-red-500 text-white rounded-lg hover:bg-red-400 transition duration-200"
onClick={() => setIsCurlPopupOpen(false)}
>
Close
</button>
</div>
</div>
</div>
)}
</div>
);
};
53 changes: 53 additions & 0 deletions demos/next/src/components/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use client';

import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { CheckIcon, ClipboardCopyIcon } from '@heroicons/react/outline';
import { useState } from 'react';
import { CodeBlock } from '@/reducers/app';


export const CodeComponent: React.FC<{ content: CodeBlock }> = ({ content }) => {
const [copied, setCopied] = useState(false);

const copyToClipboard = (text: string) => {
navigator.clipboard.writeText(text).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
});
};

return (
<div className="relative mb-6">
<SyntaxHighlighter
language={content.language || 'javascript'}
style={oneDark}
showLineNumbers
customStyle={{
borderRadius: '0.5rem',
padding: '1.5rem',
backgroundColor: '#282c34',
fontSize: '0.875rem',
overflowX: 'auto',
}}
lineNumberStyle={{ color: '#6c757d' }}
>
{content.code} + xd
</SyntaxHighlighter>
<button
onClick={() => copyToClipboard(content.code)}
className="absolute top-4 right-4 bg-blue-600 hover:bg-blue-500 text-white font-medium py-1 px-2 rounded transition duration-200 flex items-center"
>
{copied ? (
<>
<CheckIcon className="w-4 h-4 mr-1" /> Copied
</>
) : (
<>
<ClipboardCopyIcon className="w-4 h-4 mr-1" /> Copy
</>
)}
</button>
</div>
);
};
6 changes: 6 additions & 0 deletions demos/next/src/components/InteractiveComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Component } from "@/reducers/app";

export const InteractiveComponent: React.FC<{ content: Component }> = props => {
const { content } = props;
return content({})
}
Loading

0 comments on commit 0eb88b2

Please sign in to comment.