Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge pull request #74 from Readme-Monster/Feature-41 #75

Merged
merged 1 commit into from
May 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,625 changes: 2,404 additions & 2,221 deletions .pnp.cjs

Large diffs are not rendered by default.

Binary file removed .yarn/install-state.gz
Binary file not shown.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@
"@dnd-kit/utilities": "^3.2.2",
"@testing-library/user-event": "^14.5.2",
"@uiw/react-md-editor": "^4.0.4",
"axios": "^1.6.8",
"bootstrap": "^5.3.3",
"clsx": "^2.1.1",
"firebase": "^10.11.1",
"openai": "^4.40.2",
"qs": "^6.12.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
Expand Down
103 changes: 103 additions & 0 deletions src/components/Editor/Components/AiGenerator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck

import React, { useEffect } from "react";
import axios from "axios";
import { useState } from "react";
import OpenAI from "openai";

const AiGenerator = () => {
const githubToken = process.env.REACT_APP_GITHUB_API_KEY;
const [repos, setRepos] = useState({});
const [response, setResponse] = useState("");

const getRepos = async (username: string, token: string) => {
try {
const response = await axios.get("https://api.github.com/repos/Readme-Monster/readme-monster", {
headers: { Authorization: `token ${githubToken}` },
});
console.log("responseresponse", response.data);
setRepos(response.data);
if (response) {
console.log(Object.keys(response.data).length);
if (Object.keys(response.data).length) {
console.log("여기여기");
const aiResponse = await createReadme(response.data);
console.log("aiResponse", aiResponse);
setResponse(aiResponse.choices[0].text.trim());
// return response.data;
}
}
} catch (error) {
console.error("Error fetching repos:", error);
return null;
}
};

async function createReadme({ name, description, language, contributors, html_url }): Promise<string> {
const openai = new OpenAI({
apiKey: process.env.REACT_APP_OPENAI_API_KEY,
dangerouslyAllowBrowser: true,
});

console.log(name, description, language, contributors, html_url);

// prompt 관련 내용
const prompt = `${html_url}

이 레포지토리를 설명하는 리드미를 작성할거야. 지금부터 설명하는 내용을 마크다운 문법으로 작성해줘.

프로젝트 제목인 ${name}는 가장 큰 h1으로 맨 위에 써줘.
제목에 어울리는 것 같은 이모지도 하나 추천해서 제목 왼쪽에 넣어주면 좋겠어.

그 다음으로는 h3(###)로 "프로젝트 개요"라고 쓴 다음, 어울리는 이모지를 하나 제목 왼쪽에 넣어줘.
그리고 다음에 오는 텍스트를 그대로 넣어줘. unordered list를 사용해서 넣으면 돼.
${description}


다음으로는 h3(###)로 "기술 스택"이라고 쓴 다음, 어울리는 이모지를 하나 제목 왼쪽에 넣어줘.

${language}를 각각 https://simpleicons.org/ 사이트에서 검색하여 검색 결과를 바탕으로 다음 코드를 채워서 이어 써주면 돼. 줄바꿈 없이 붙여서 써줘야해.

![Badge](https://img.shields.io/badge/로고이름-색상코드?style=flat&logo=로고이름&logoColor=white)
로고 이름에는 검색 결과에서 가장 첫번째로 찾은 텍스트를, 색상 코드에서는 검색 결과에 나온 #로 시작하는 컬러 헥사코드 6자리를 #을 제외하고 넣어줘.


그 다음으로는 h3(###)로 "참여 팀원"이라고 쓴 다음, 어울리는 이모지를 하나 제목 왼쪽에 넣어줘.
그리고 아래 오는 내용을 표로 만들어주면 좋겠어.
1번째 행에 각각의 팀원 이름을 넣어줘.
2번째 행에는 각 팀원이 맡은 기능을 쓸거야.

팀원 이름과 각 팀원이 맡은 기능은 다음과 같아.
${contributors}`;
// `
// Please create the README for GitHub in Korean.. This repository named ${name} and description is ${description}. The main language used is ${language}. The Contributors are ${contributors}. For the rest of the information, refer to the repo link ${html_url}, summarize it, and add it yourself to the readme file.`;

console.log("openai", openai);
try {
const response = await openai.completions.create({
// messages: [{ role: "user", content: `${prompt}` }],
model: "gpt-3.5-turbo-instruct",
prompt: prompt,
max_tokens: 150,
});
console.log(response);
return response;

// return response.data.choices[0].text.trim();
} catch (error) {
console.log("Error generating README:", error);
throw new Error("Failed to generate README");
}
}

console.log("open api 에서 넘겨주는 응답", response);
console.log("repos", repos);
return (
<div className="border-3">
<button onClick={getRepos}>readme생성</button>
</div>
);
};

export default AiGenerator;
149 changes: 93 additions & 56 deletions src/components/Editor/Components/EditSection.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,100 @@
import React, { useState } from "react";
import { List, Reset, TrashCan } from "@carbon/icons-react";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import clsx from "clsx";

interface Props {
id: number;
title: string | undefined;
markdown: string | undefined;
onDeleteSection: (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => void;
}

const EditSection = ({ id, title, markdown, onDeleteSection }: Props) => {
const [hover, setHover] = useState<boolean>(false);
const onMouseEnter = () => setHover(true);
const onMouseLeave = () => setHover(false);

const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });

const style = {
transition,
transform: CSS.Transform.toString(transform),
import React, { useEffect, useState } from "react";
import EditSection from "./EditSection";
import {
DndContext,
closestCenter,
useSensor,
useSensors,
KeyboardSensor,
MouseSensor,
TouchSensor,
DragEndEvent,
} from "@dnd-kit/core";
import {
SortableContext,
verticalListSortingStrategy,
arrayMove,
sortableKeyboardCoordinates,
} from "@dnd-kit/sortable";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import { useSection } from "context/SectionContext";
import { KeyNameType, SectionsType } from "../types";

const EditSections = ({ keyName }: KeyNameType) => {
const { value, setValue } = useSection();
const [sections, setSections] = useState<SectionsType[]>([]);

const getIndex = (id: number) => sections.findIndex(el => el.id === id);

const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event;
if (active.id === over?.id) return;
setSections(sections => {
const oldIndex = getIndex(active.id as number);
const newIndex = getIndex(over?.id as number);

return arrayMove(sections, oldIndex, newIndex);
});
};

const sensors = useSensors(
useSensor(TouchSensor),
useSensor(MouseSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
}),
);

const onDeleteSection = (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => {
e.stopPropagation();
setSections(prev => prev.filter(el => el.id !== targetId));
};

const onResetSection = (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => {
e.stopPropagation();
};

useEffect(() => {
const sectionsList = JSON.parse(localStorage.getItem(`${keyName}`) || "[]");
if (sectionsList.length > 0) {
setSections(sectionsList);
}
}, []);

useEffect(() => {
localStorage.setItem(`${keyName}`, JSON.stringify(sections));
const sectionsList = JSON.parse(localStorage.getItem(`${keyName}`) || "[]");
const markdownList = sectionsList.map((el: SectionsType) => el.markdown).join("");
setValue(markdownList);
}, [sections]);

return (
<div
ref={setNodeRef}
{...attributes}
style={style}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
className={clsx(
"w-full h-[45px] py-[8px] px-[12px]",
"flex flex-row gap-[10px] items-center",
"rounded-[8px] border-solid border bg-white border-[#F1F3F5] drop-shadow-[0_1px_1px_rgba(173,181,189,0.25)]",
"cursor-pointer",
{
"focus:outline-none focus:ring-2 focus:ring-textBlue": true,
},
)}
>
<List {...listeners} size={25} className="fill-textSecondary min-w-[25px]" />
<p className="text-textPrimary mb-0 truncate">{title}</p>
{hover && (
<div className="flex flex-row gap-[10px] ml-auto">
<button>
<Reset size={20} className="fill-[#ADB5BD]" onClick={() => alert("rest")} />
</button>
<button
onClick={e => {
onDeleteSection(e, id);
}}
>
<TrashCan size={20} className="fill-textPrimary" />
</button>
</div>
)}
<div className="flex flex-col gap-[10px] px-[10px]">
<div className="flex-Center flex-row justify-between min-h-[30px]">
<p className="text-textPrimary ml-[5px] mb-0 text-sm">Edit Section</p>
</div>
<div className="flex flex-col gap-[10px] h-full">
<DndContext
sensors={sensors}
onDragEnd={handleDragEnd}
collisionDetection={closestCenter}
modifiers={[restrictToVerticalAxis]}
>
<SortableContext items={sections} strategy={verticalListSortingStrategy}>
{/* {sections.map(section => (
<EditSection
key={section.id}
title={section.title}
id={section.id}
markdown={section.markdown}
onDeleteSection={onDeleteSection}
/>
))} */}
</SortableContext>
</DndContext>
</div>
</div>
);
};

export default EditSection;
export default EditSections;
4 changes: 2 additions & 2 deletions src/components/Editor/Components/EditSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ const EditSections = ({ keyName }: KeyNameType) => {
modifiers={[restrictToVerticalAxis]}
>
<SortableContext items={sections} strategy={verticalListSortingStrategy}>
{sections.map(section => (
{/* {sections.map(section => (
<EditSection
key={section.id}
title={section.title}
id={section.id}
markdown={section.markdown}
onDeleteSection={onDeleteSection}
/>
))}
))} */}
</SortableContext>
</DndContext>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/components/Editor/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface SectionsType {
id: number;
title: string | undefined;
markdown: string | undefined;
onDeleteSection: (e: React.MouseEvent<HTMLElement, MouseEvent>, targetId: number) => void;
}

export interface KeyNameType {
Expand Down
2 changes: 2 additions & 0 deletions src/pages/HomePage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AiGenerator from "components/Editor/Components/AiGenerator";
import React from "react";
import { RoutePath, useRouter } from "../routing";

Expand Down Expand Up @@ -43,6 +44,7 @@ function HomePage() {
description="더 알아보기"
testId="secondButton"
/>
<AiGenerator />
</div>
</div>
);
Expand Down
Loading
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.