mirror of
https://github.com/hilmanski/contentswift.git
synced 2026-08-24 02:24:13 -05:00
remove git submodule inside fe dir
This commit is contained in:
@@ -44,6 +44,13 @@ archive.zip
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# Since frontend (nextjs) in a subfolder
|
||||
.next/
|
||||
./out/
|
||||
# Ignore node_modules in any subfolder
|
||||
**/node_modules/
|
||||
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
|
||||
-1
Submodule frontend-crt deleted from 2c106aadb5
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
@@ -0,0 +1,4 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
module.exports = nextConfig
|
||||
Generated
+4345
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "frontend-crt",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "latest",
|
||||
"react-dom": "latest",
|
||||
"next": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "latest",
|
||||
"@types/react": "latest",
|
||||
"@types/node": "latest",
|
||||
"@types/react-dom": "latest",
|
||||
"autoprefixer": "latest",
|
||||
"postcss": "latest",
|
||||
"tailwindcss": "latest",
|
||||
"eslint": "latest",
|
||||
"eslint-config-next": "latest"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
After Width: | Height: | Size: 629 B |
@@ -0,0 +1,109 @@
|
||||
import Terms from "@/components/Terms";
|
||||
|
||||
async function getData(id: string) {
|
||||
const res = await fetch(`http://localhost:8000/post/${id}`)
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to fetch data')
|
||||
}
|
||||
|
||||
return res.json()
|
||||
}
|
||||
|
||||
|
||||
export default async function Editor({
|
||||
children, params
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: {
|
||||
id: string;
|
||||
}
|
||||
}) {
|
||||
const data = await getData(params.id);
|
||||
const searchResult = data.search_result
|
||||
|
||||
return (
|
||||
<div className="my-10 max-w-6xl mx-auto">
|
||||
<h1>Editor document with id: {params.id}</h1>
|
||||
|
||||
<div>
|
||||
Title: {data.title}
|
||||
</div>
|
||||
|
||||
{
|
||||
searchResult.answer_box && (
|
||||
<section className="my-5 p-3 border border-emerald-600 text-sm">
|
||||
<h3 className="text-emerald-700 ">Answer Box (Featued Snippet)</h3>
|
||||
<p className="italic">
|
||||
{
|
||||
searchResult.answer_box.snippet
|
||||
}
|
||||
</p>
|
||||
|
||||
{
|
||||
searchResult.answer_box.list && (
|
||||
<div>
|
||||
<p className="text-bold italic">Info: Features snippet is a list</p>
|
||||
{
|
||||
searchResult.answer_box.list.map((item, index) => {
|
||||
return (
|
||||
<div key={index}>
|
||||
<p>{item}</p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<p>
|
||||
<a className="text-sky-700"
|
||||
href={searchResult.answer_box.link}>
|
||||
source
|
||||
</a>
|
||||
</p>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
searchResult.related_questions.length > 0 && (
|
||||
<section className="my-5 p-3 border border-emerald-600 text-sm">
|
||||
<h3 className="text-emerald-700 mb-2">Related Questions</h3>
|
||||
{
|
||||
searchResult.related_questions.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className="mb-1">
|
||||
<p className="font-bold"> {item.question} </p>
|
||||
<p className="italic"> {item.snippet} </p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
searchResult.related_searches.length > 0 && (
|
||||
<section className="my-5 p-3 border border-emerald-600 text-sm">
|
||||
<h3 className="text-emerald-700 mb-2">Related Searches</h3>
|
||||
{
|
||||
searchResult.related_searches.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className="mb-1">
|
||||
<p className=""> {item.query} </p>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
<Terms id={params.id} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,4 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import './globals.css'
|
||||
import type { Metadata } from 'next'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Content Research Tool',
|
||||
description: 'Generated by create next app',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import Loading from "../components/loading";
|
||||
|
||||
|
||||
export default function Home() {
|
||||
|
||||
const [keyword, setKeyword] = useState('how to write an article');
|
||||
const [googleDomain, setGoogleDomain] = useState('google.com');
|
||||
const [searchLocation, setSearchLocation] = useState('us');
|
||||
const [searchLang, setSearchLang] = useState('en');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [resultsAvailable, setResultsAvailable] = useState(false);
|
||||
const [organicResults, setOrganicResults] = useState([]);
|
||||
const [choosenLinks, setChoosenLinks] = useState([])
|
||||
const [postId, setPostId] = useState(null);
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
console.log(keyword);
|
||||
console.log(googleDomain);
|
||||
console.log(searchLocation);
|
||||
console.log(searchLang);
|
||||
|
||||
setLoading(true);
|
||||
|
||||
// fetch data from api
|
||||
fetch('http://localhost:8000/search', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
keyword: keyword,
|
||||
googleDomain: googleDomain,
|
||||
searchLocation: searchLocation,
|
||||
searchLang: searchLang,
|
||||
}),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Success:', data);
|
||||
setLoading(false);
|
||||
setResultsAvailable(true)
|
||||
setOrganicResults(data.search_result.organic_results)
|
||||
|
||||
// add first 5 organic_result .link to choosenLinks
|
||||
setChoosenLinks(data.search_result.organic_results.slice(0, 5).map((result) => result.link))
|
||||
setPostId(data.post_id)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
const handleArticleChange = (e, index) => {
|
||||
if(e.target.checked) {
|
||||
// add to choosenLinks
|
||||
setChoosenLinks([...choosenLinks, organicResults[index].link])
|
||||
} else {
|
||||
// remove from choosenLinks
|
||||
setChoosenLinks(choosenLinks.filter((article) => article !== organicResults[index].link))
|
||||
}
|
||||
}
|
||||
|
||||
const goToEditorPage = () => {
|
||||
// save choosen article to DB
|
||||
fetch('http://localhost:8000/links', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
postId, choosenLinks,
|
||||
}),
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Success:', data);
|
||||
window.location.href = `/editor/${postId}`
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error);
|
||||
alert('Error saving to DB')
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<main className='my-10 mx-auto max-w-5xl'>
|
||||
<h1 className="text-xl">Content Research Tool</h1>
|
||||
|
||||
{
|
||||
loading ? <Loading /> : null
|
||||
}
|
||||
|
||||
{
|
||||
(resultsAvailable && organicResults.length > 0) && (
|
||||
<div className="my-5">
|
||||
<h2 className="mb-5">
|
||||
These are the top 20 articles that rank for your keyword.
|
||||
<b>Choose 5-10 articles as comparison</b></h2>
|
||||
<button className="bg-blue-500 text-white p-3 mb-5 rounded-lg"
|
||||
onClick={goToEditorPage}>
|
||||
Continue
|
||||
</button>
|
||||
{
|
||||
organicResults.map((result, index) => (
|
||||
<div key={index}
|
||||
className="mb-3 border-l pl-2 flex items-center justify-between">
|
||||
|
||||
<label htmlFor="url_to_scrape"
|
||||
className="mr-5">
|
||||
<input type="checkbox"
|
||||
id="url_to_scrape"
|
||||
onChange={(e) => handleArticleChange(e, index)}
|
||||
checked={choosenLinks.includes(result.link)}
|
||||
className="" />
|
||||
</label>
|
||||
|
||||
<p className="font-bold w-5/12">{result.title}</p>
|
||||
<a className="w-5/12 underline text-sky-500"
|
||||
href={result.link}>
|
||||
{result.link}
|
||||
</a>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
<div className="my-10">
|
||||
<p className="font-bold">New Content</p>
|
||||
|
||||
<div className="my-5">
|
||||
<p className="">keyword you want to rank</p>
|
||||
<input className="border border-gray-400 rounded w-full p-2"
|
||||
placeholder="how to write an article"
|
||||
value={keyword}
|
||||
onChange={e => setKeyword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="my-5">
|
||||
{/* select option for search engine */}
|
||||
<p className="">Search Engine (Google domain).
|
||||
<a className="text-blue-600 underline ml-2"
|
||||
href="https://serpapi.com/google-domains">See Reference</a>
|
||||
</p>
|
||||
<input className="border border-gray-400 rounded w-full p-2"
|
||||
placeholder="google.com"
|
||||
value={googleDomain}
|
||||
onChange={e => setGoogleDomain(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="my-5">
|
||||
<p className="">Search Location
|
||||
<a className="text-blue-600 underline ml-2"
|
||||
href="https://serpapi.com/google-countries">See Reference</a>
|
||||
</p>
|
||||
<input className="border border-gray-400 rounded w-full p-2"
|
||||
placeholder="us"
|
||||
value={searchLocation}
|
||||
onChange={e => setSearchLocation(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="my-5">
|
||||
<p className="">Search Lang
|
||||
<a className="text-blue-600 underline ml-2"
|
||||
href="https://serpapi.com/google-languages">See Reference</a>
|
||||
</p>
|
||||
<input className="border border-gray-400 rounded w-full p-2"
|
||||
placeholder="en"
|
||||
value={searchLang}
|
||||
onChange={e => setSearchLang(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* submit btn */}
|
||||
<div className="my-5">
|
||||
<button
|
||||
className="w-full bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* End of form */}
|
||||
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="absolute top-0 left-0 flex justify-center items-center
|
||||
h-screen w-screen bg-gray-100 dark:bg-gray-800">
|
||||
<div role="status">
|
||||
<svg aria-hidden="true" className="w-8 h-8 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
||||
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
||||
</svg>
|
||||
<span className="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function Terms({id} : {id: string}) {
|
||||
|
||||
const [result, setResult] = useState({} as any)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
getData(id).then((data) => {
|
||||
setResult(data.data)
|
||||
})
|
||||
}, [])
|
||||
|
||||
async function getData(id: string) {
|
||||
const res = await fetch(`http://localhost:8000/scrape/${id}`)
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error('Failed to fetch data')
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
return res.json()
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<section className="my-5">
|
||||
<h2>Terms (Take up to 1 minute)</h2>
|
||||
Loading...
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="my-5">
|
||||
<h2>Terms </h2>
|
||||
<p> Average words length: {result.averageWords} </p>
|
||||
|
||||
{
|
||||
result.topKeywords && (
|
||||
<div className="my-5">
|
||||
<h3>Top Keywords</h3>
|
||||
<ul>
|
||||
{
|
||||
result.topKeywords.map((term: string, index: number) => {
|
||||
return (
|
||||
<li className="inline-block bg-gray-200 mb-1 rounded-full px-3 py-1 text-sm text-gray-700 mr-2"
|
||||
key={index}>
|
||||
{term[0]}: {parseInt(term[1]) / result.contentInfo.length}
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
result.contentInfo && (
|
||||
<div className="my-5 border p-5 border-green-700 text-sm">
|
||||
<h3>Competitor reference</h3>
|
||||
<div className="flex justify-between space-x-5 overflow-x-scroll">
|
||||
{
|
||||
result.contentInfo.map((site: string, index: number) => {
|
||||
return (
|
||||
<div key={index}
|
||||
className="border border-green-200 p-2">
|
||||
<p>
|
||||
Site:
|
||||
<a className="text-sky-700"
|
||||
href={site.link}>
|
||||
{site.link.substring(0, 30)}...
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Words: {site.totalWords}
|
||||
</p>
|
||||
|
||||
{
|
||||
site.headings.length > 0 && (
|
||||
<div>
|
||||
<p>Headings</p>
|
||||
{
|
||||
site.headings.map((heading: string, index: number) => {
|
||||
return (
|
||||
<p key={index}>
|
||||
{heading.tag}: {heading.text}
|
||||
</p>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
site.keywords.length > 0 && (
|
||||
<div>
|
||||
<p>keywords</p>
|
||||
{
|
||||
site.keywords.map((keyword: string, index: number) => {
|
||||
return (
|
||||
<li key={index}
|
||||
className="inline-block bg-gray-200 mb-1 rounded-full px-3 py-1 text-sm text-gray-700 mr-2">
|
||||
{keyword.word}: {keyword.frequency}
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Config } from 'tailwindcss'
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
||||
'gradient-conic':
|
||||
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
export default config
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user