├── src
├── vite-env.d.ts
├── App.tsx
├── global.css
├── main.tsx
├── components
│ ├── WidgetForm
│ │ ├── Loading.tsx
│ │ ├── Steps
│ │ │ ├── FeedbackTypeStep.tsx
│ │ │ ├── FeedbackSuccessStep.tsx
│ │ │ └── FeedbackContentStep.tsx
│ │ ├── ScreenshotButton.tsx
│ │ └── index.tsx
│ ├── CloseButton.tsx
│ └── Widget.tsx
└── assets
│ ├── thought.svg
│ ├── idea.svg
│ └── bug.svg
├── postcss.config.js
├── tsconfig.node.json
├── vite.config.ts
├── index.html
├── .gitignore
├── tailwind.config.js
├── tsconfig.json
└── package.json
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Widget } from "./components/Widget";
2 |
3 | export function App() {
4 | return
5 | }
--------------------------------------------------------------------------------
/src/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | @apply bg-[#09090A] text-zinc-100
7 | }
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "esnext",
5 | "moduleResolution": "node"
6 | },
7 | "include": ["vite.config.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()]
7 | })
8 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import { App } from './App'
4 |
5 | import './global.css'
6 |
7 | ReactDOM.createRoot(document.getElementById('root')!).render(
8 |
9 |
10 |
11 | )
12 |
13 |
--------------------------------------------------------------------------------
/src/components/WidgetForm/Loading.tsx:
--------------------------------------------------------------------------------
1 | import { CircleNotch } from "phosphor-react";
2 |
3 | export function Loading() {
4 | return (
5 |
6 |
7 |
8 | )
9 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vite App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/src/components/CloseButton.tsx:
--------------------------------------------------------------------------------
1 | import { Popover } from '@headlessui/react'
2 | import { X } from 'phosphor-react'
3 |
4 | export function CloseButton() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const { borderRadius } = require('tailwindcss/defaultTheme');
2 |
3 | module.exports = {
4 | content: ["./src/**/*.tsx"],
5 | theme: {
6 | extend: {
7 | colors: {
8 | brand: {
9 | 300: '#996DFF',
10 | 500: '#8257e6',
11 | }
12 | },
13 | borderRadius: {
14 | md: '4px',
15 | }
16 | },
17 | },
18 | plugins: [
19 | require('@tailwindcss/forms'),
20 | require('tailwind-scrollbar'),
21 | ],
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/src/assets/thought.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ts",
3 | "private": true,
4 | "version": "0.0.0",
5 | "scripts": {
6 | "dev": "vite",
7 | "build": "tsc && vite build",
8 | "preview": "vite preview"
9 | },
10 | "dependencies": {
11 | "@headlessui/react": "^1.6.1",
12 | "html2canvas": "^1.4.1",
13 | "phosphor-react": "^1.4.1",
14 | "react": "^18.0.0",
15 | "react-dom": "^18.0.0"
16 | },
17 | "devDependencies": {
18 | "@tailwindcss/forms": "^0.5.1",
19 | "@types/react": "^18.0.0",
20 | "@types/react-dom": "^18.0.0",
21 | "@vitejs/plugin-react": "^1.3.0",
22 | "autoprefixer": "^10.4.7",
23 | "postcss": "^8.4.13",
24 | "tailwind-scrollbar": "^1.3.1",
25 | "tailwindcss": "^3.0.24",
26 | "typescript": "^4.6.3",
27 | "vite": "^2.9.7"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Widget.tsx:
--------------------------------------------------------------------------------
1 | import { ChatTeardropDots } from "phosphor-react"
2 | import { Popover } from "@headlessui/react"
3 | import { WidgetForm } from './WidgetForm'
4 |
5 | export function Widget() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Feedback
18 |
19 |
20 |
21 |
22 | )
23 | }
--------------------------------------------------------------------------------
/src/components/WidgetForm/Steps/FeedbackTypeStep.tsx:
--------------------------------------------------------------------------------
1 | import { FeedbackType, feedbackTypes } from ".."
2 | import { CloseButton } from "../../CloseButton";
3 |
4 | interface FeedbackTypeStepProps {
5 | onFeedbackTypeChanged: (type: FeedbackType) => void;
6 | }
7 |
8 | export function FeedbackTypeStep({ onFeedbackTypeChanged }: FeedbackTypeStepProps) {
9 | return (
10 | <>
11 |
12 | Deixe seu feedback
13 |
14 |
15 |
16 |
17 | {Object.entries(feedbackTypes).map(([key, value]) => {
18 | return (
19 |
27 | )
28 | })}
29 |
30 | >
31 | )
32 | }
--------------------------------------------------------------------------------
/src/assets/idea.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/components/WidgetForm/Steps/FeedbackSuccessStep.tsx:
--------------------------------------------------------------------------------
1 | import { CloseButton } from "../../CloseButton";
2 |
3 | interface FeedbackSuccessStepProps {
4 | onFeedbackRestartRequested: () => void;
5 | }
6 |
7 | export function FeedbackSuccessStep({ onFeedbackRestartRequested }: FeedbackSuccessStepProps) {
8 | return (
9 | <>
10 |
13 |
14 |
15 |
19 |
20 |
Agradecemos o feedback!
21 |
22 |
28 |
29 | >
30 | )
31 | }
--------------------------------------------------------------------------------
/src/components/WidgetForm/ScreenshotButton.tsx:
--------------------------------------------------------------------------------
1 | import { Camera, Trash } from "phosphor-react";
2 | import html2canvas from "html2canvas";
3 | import { useState } from "react";
4 | import { Loading } from "./Loading";
5 |
6 | interface ScreenshotButtonProps {
7 | screenshot: string | null;
8 | onScreenshotTook: (screenshot: string | null) => void;
9 | }
10 |
11 | export function ScreenshotButton({ screenshot, onScreenshotTook }: ScreenshotButtonProps) {
12 | const [isTakingScreenshot, setIsTakingScreenshot] = useState(false)
13 |
14 | async function handleTakeScreenshot() {
15 | setIsTakingScreenshot(true);
16 |
17 | const canvas = await html2canvas(document.querySelector('html')!)
18 | const base64image = canvas.toDataURL('image/png')
19 |
20 | onScreenshotTook(base64image)
21 | setIsTakingScreenshot(false);
22 | }
23 |
24 | if (screenshot) {
25 | return (
26 |
39 | )
40 | }
41 | return (
42 |
50 | )
51 | }
--------------------------------------------------------------------------------
/src/components/WidgetForm/index.tsx:
--------------------------------------------------------------------------------
1 | import { CloseButton } from "../CloseButton";
2 |
3 | import bugImageUrl from '../../assets/bug.svg';
4 | import ideaImageUrl from '../../assets/idea.svg';
5 | import thoughImageUrl from '../../assets/thought.svg';
6 | import { useState } from "react";
7 | import { FeedbackTypeStep } from "./Steps/FeedbackTypeStep";
8 | import { FeedbackContentStep } from "./Steps/FeedbackContentStep";
9 | import { FeedbackSuccessStep } from "./Steps/FeedbackSuccessStep";
10 |
11 | export const feedbackTypes = {
12 | BUG: {
13 | title: 'Problema',
14 | image: {
15 | source: bugImageUrl,
16 | alt: 'Imagem de um inseto'
17 | },
18 | },
19 | IDEA: {
20 | title: 'Ideia',
21 | image: {
22 | source: ideaImageUrl,
23 | alt: 'Imagem de uma lâmpada'
24 | },
25 | },
26 | OTHER: {
27 | title: 'Outro',
28 | image: {
29 | source: thoughImageUrl,
30 | alt: 'Imagem de um balão de pensamento'
31 | },
32 | },
33 | }
34 | export type FeedbackType = keyof typeof feedbackTypes;
35 |
36 | export function WidgetForm() {
37 | const [feedbackType, setFeedbackType] = useState(null)
38 | const [feedbackSent, setFeedbackSent] = useState(false);
39 |
40 | function handleRestartFeedback() {
41 | setFeedbackSent(false);
42 | setFeedbackType(null);
43 | }
44 |
45 | return (
46 |
47 | {feedbackSent ? (
) :
48 | (
49 | <>
50 | {!feedbackType ? (
51 |
52 | ) : (
53 |
setFeedbackSent(true)} />
57 | )
58 | }
59 | >
60 | )}
61 |
64 |
65 | )
66 | }
--------------------------------------------------------------------------------
/src/components/WidgetForm/Steps/FeedbackContentStep.tsx:
--------------------------------------------------------------------------------
1 | import { ArrowLeft, Camera } from "phosphor-react";
2 | import { FormEvent, useState } from "react";
3 | import { FeedbackType, feedbackTypes } from ".."
4 | import { CloseButton } from "../../CloseButton"
5 | import { ScreenshotButton } from "../ScreenshotButton";
6 |
7 | interface FeedbackContentStepProps {
8 | feedbackType: FeedbackType;
9 | onFeedbackRestartRequested: () => void;
10 | onFeedbackSent: () => void;
11 | }
12 |
13 | export function FeedbackContentStep({
14 | feedbackType, onFeedbackRestartRequested, onFeedbackSent }: FeedbackContentStepProps) {
15 | const [screenshot, setScreenshot] = useState(null)
16 | const [comment, setComment] = useState('');
17 |
18 | const feedbackTypeInfo = feedbackTypes[feedbackType];
19 |
20 | function handleSubmitFeedback(event: FormEvent) {
21 | event.preventDefault();
22 |
23 | console.log({
24 | screenshot,
25 | comment,
26 | });
27 |
28 | onFeedbackSent();
29 | }
30 |
31 | return (
32 | <>
33 |
34 |
41 |
42 |
43 |
44 | {feedbackTypeInfo.title}
45 |
46 |
47 |
48 |
49 |
50 |
70 | >
71 | )
72 | }
--------------------------------------------------------------------------------
/src/assets/bug.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------