From d9d7ca7e04d65a700726312038b04e3287f11877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:19:51 +0100 Subject: [PATCH 01/31] chore: update npm module documentation --- content/docs/basic-usage.mdx | 11 +- content/docs/examples.mdx | 264 ---------------------------------- content/docs/index.mdx | 4 - content/docs/installation.mdx | 3 - 4 files changed, 1 insertion(+), 281 deletions(-) diff --git a/content/docs/basic-usage.mdx b/content/docs/basic-usage.mdx index 29c98cc..d7c0e5a 100644 --- a/content/docs/basic-usage.mdx +++ b/content/docs/basic-usage.mdx @@ -47,8 +47,6 @@ After creating a test, you can add steps to it: await reporter.step("Step name", async () => { // Your step implementation goes here // For example: await page.click('.button'); - - return true; // or any value }); ``` @@ -70,7 +68,6 @@ The `step` method accepts the following parameters: ```javascript await reporter.step("Click login button", async () => { await page.click('.login-button'); - return true; }); ``` @@ -162,27 +159,22 @@ async function runTest() { // Add test steps await reporter.step("Navigate to login page", () => { console.log("Navigating to login page..."); - return true; }); await reporter.step("Enter username", () => { console.log("Entering username..."); - return true; }); await reporter.step("Enter password", () => { console.log("Entering password..."); - return true; }); await reporter.step("Click login button", () => { console.log("Clicking login button..."); - return true; }); await reporter.step("Verify redirect to dashboard", () => { console.log("Verifying redirect..."); - return true; }); // End the test and get results @@ -227,5 +219,4 @@ try { ## Next Steps Now that you understand the basics, check out: -- [Examples](/docs/examples) for integration with specific frameworks -- [API Reference](/docs/api-reference) for detailed API documentation \ No newline at end of file +- [Examples](/docs/examples) for integration with specific frameworks \ No newline at end of file diff --git a/content/docs/examples.mdx b/content/docs/examples.mdx index 7534389..05a147d 100644 --- a/content/docs/examples.mdx +++ b/content/docs/examples.mdx @@ -34,18 +34,15 @@ test('Product search test', async ({ page, browser }) => { // Test steps await reporter.step('Navigate to homepage', async () => { await page.goto('https://example.com'); - return true; }); await reporter.step('Click search box', async () => { await page.click('.search-box'); - return true; }); await reporter.step('Enter search query', async () => { await page.fill('.search-input', 'test product'); await page.press('.search-input', 'Enter'); - return true; }); // Take a screenshot of the search results @@ -112,18 +109,15 @@ import { test, expect } from './fixtures'; test('User login test', async ({ page, qaReporter }) => { await qaReporter.step('Navigate to login page', async () => { await page.goto('https://example.com/login'); - return true; }); await qaReporter.step('Enter credentials', async () => { await page.fill('#username', 'testuser'); await page.fill('#password', 'password123'); - return true; }); await qaReporter.step('Click login button', async () => { await page.click('#login-button'); - return true; }); await qaReporter.step('Verify successful login', async () => { @@ -158,162 +152,14 @@ describe('User functions', () => { test('should register a new user', async () => { await reporter.step('Fill out registration form', () => { // Registration form logic - return true; }); await reporter.step('Submit registration form', () => { // Form submission logic - return true; }); await reporter.step('Verify confirmation email', () => { // Email verification logic - return true; - }); - }); -}); -``` - -### Jest with Custom Reporter - -You can also create a custom Jest reporter to automatically handle QAFlow reporting: - -```javascript -// qaflow-jest-reporter.js -import reporter from '@qaflow/report'; - -class QAFlowJestReporter { - constructor(globalConfig, options) { - this.globalConfig = globalConfig; - this.options = options; - this.currentTest = null; - } - - onRunStart() { - // Initialize QAFlow if needed - if (this.options.apiKey) { - reporter.initialize(this.options.apiKey); - } - } - - onTestStart(test) { - this.currentTest = test; - - reporter.createTest( - test.title, - test.fullName, - { author: this.options.author || 'Jest Tester', email: this.options.email || 'tester@example.com' }, - { name: 'Jest', version: process.version, os: process.platform } - ); - } - - async onTestResult(test, testResult) { - if (testResult.status === 'failed') { - // Record the failure - await reporter.step('Test execution', false, { - description: testResult.failureMessage - }); - } else { - // Record the success - await reporter.step('Test execution', true); - } - - await reporter.end(); - } - - onRunComplete() { - // Cleanup if needed - } -} - -module.exports = QAFlowJestReporter; -``` - -Configure in your Jest config: - -```javascript -// jest.config.js -module.exports = { - reporters: [ - 'default', - ['./qaflow-jest-reporter.js', { - apiKey: process.env.QAFLOW_API_KEY, - author: 'Jest Test Runner', - email: 'test@example.com' - }] - ] -}; -``` - -## Cypress Integration - -### Basic Cypress Example - -```javascript -// cypress/support/e2e.js -import reporter from '@qaflow/report'; - -// Initialize QAFlow Reporter -beforeEach(() => { - const testName = Cypress.currentTest.title; - const testDescription = `${Cypress.currentTest.titlePath.join(' > ')}`; - - reporter.createTest( - testName, - testDescription, - { author: 'Cypress Tester', email: 'cypress@example.com' }, - { - name: 'Cypress', - browser: Cypress.browser.name, - version: Cypress.browser.version, - os: Cypress.platform - } - ); -}); - -// End test after completion -afterEach(() => { - reporter.end(); -}); - -// Define a custom Cypress command for steps -Cypress.Commands.add('qaStep', (name, fn, options) => { - return cy.wrap(null).then(async () => { - return reporter.step(name, fn, options); - }); -}); -``` - -In your test file: - -```javascript -// cypress/e2e/login.cy.js -describe('Login functionality', () => { - it('Should log in successfully with valid credentials', () => { - cy.visit('/login'); - - cy.qaStep('Navigate to login page', () => { - return true; - }); - - cy.qaStep('Enter username', () => { - cy.get('#username').type('testuser'); - return true; - }); - - cy.qaStep('Enter password', () => { - cy.get('#password').type('password123'); - return true; - }); - - cy.qaStep('Click login button', () => { - cy.get('#login-button').click(); - return true; - }); - - cy.qaStep('Verify successful login', () => { - cy.url().should('include', '/dashboard'); - return true; }); }); }); @@ -342,7 +188,6 @@ async function runMyCustomTest() { // Test steps await reporter.step('Initialize API client', () => { console.log('Initializing API client...'); - return true; }); await reporter.step('Generate test order', async () => { @@ -359,7 +204,6 @@ async function runMyCustomTest() { await reporter.step('Verify payment success', async () => { console.log('Verifying payment success...'); - return true; }); // End the test @@ -376,112 +220,4 @@ async function runMyCustomTest() { runMyCustomTest().catch(console.error); ``` -## Advanced Examples - -### Parallel Tests - -When running tests in parallel, ensure each test has a unique ID: - -```javascript -import reporter from '@qaflow/report'; - -// For parallel tests, create a custom instance -const myReporter = new QAFlowReport({ - apiKey: 'your-api-key-here' -}); - -async function runTest(testId) { - const test = myReporter.createTest( - `Parallel Test ${testId}`, - `Test running in parallel with ID ${testId}`, - { author: 'Parallel Tester', email: 'parallel@example.com' }, - { name: 'Node.js', version: process.version, os: process.platform } - ); - - // Add steps - await test.step(`Step 1 for test ${testId}`, async () => { - await new Promise(resolve => setTimeout(resolve, 100 * testId)); - return true; - }); - - await test.step(`Step 2 for test ${testId}`, async () => { - await new Promise(resolve => setTimeout(resolve, 50 * testId)); - return true; - }); - - // End this specific test - return test.end(); -} - -// Run 5 tests in parallel -Promise.all([1, 2, 3, 4, 5].map(id => runTest(id))) - .then(results => { - console.log('All tests completed!'); - results.forEach(result => { - console.log(`${result.name}: ${result.summary.passed}/${result.summary.total} passed`); - }); - }) - .catch(console.error); -``` - -### Nested Steps - -For more complex tests, you can create nested steps by calling `reporter.step` inside another step: - -```javascript -import reporter from '@qaflow/report'; - -async function runComplexTest() { - reporter.createTest( - 'Complex Workflow Test', - 'Tests a complex multi-step workflow', - { author: 'QA Tester', email: 'tester@example.com' }, - { name: 'Node.js', version: process.version, os: process.platform } - ); - - await reporter.step('User registration flow', async () => { - const user = { name: 'Test User', email: 'user@example.com' }; - - // Nested steps - await reporter.step('Fill registration form', () => { - console.log('Filling form...'); - return true; - }); - - await reporter.step('Submit registration', () => { - console.log('Submitting registration...'); - return true; - }); - - await reporter.step('Verify email', () => { - console.log('Verifying email...'); - return true; - }); - - return user; // Return value from parent step - }); - - await reporter.step('Product ordering flow', async () => { - // More nested steps... - await reporter.step('Search for product', () => { - return true; - }); - - await reporter.step('Add to cart', () => { - return true; - }); - - await reporter.step('Checkout', () => { - return true; - }); - }); - - const results = await reporter.end(); - console.log('Complex test completed!'); - console.log(results); -} - -runComplexTest().catch(console.error); -``` - These examples should help you get started with QAFlow Reporter in your testing workflows. For more detailed API documentation, see the [API Reference](/docs/api-reference) section. \ No newline at end of file diff --git a/content/docs/index.mdx b/content/docs/index.mdx index 0b39648..3fc4344 100644 --- a/content/docs/index.mdx +++ b/content/docs/index.mdx @@ -53,22 +53,18 @@ reporter.createTest( // Add test steps await reporter.step("Navigate to login page", () => { // Step implementation - return true; // Step passes }); await reporter.step("Enter username", () => { // Username entry logic - return true; }); await reporter.step("Enter password", () => { // Password entry logic - return true; }); await reporter.step("Click login button", () => { // Login button click logic - return true; }); // End the test and get results diff --git a/content/docs/installation.mdx b/content/docs/installation.mdx index 7fc673a..570d56b 100644 --- a/content/docs/installation.mdx +++ b/content/docs/installation.mdx @@ -83,8 +83,6 @@ QAFlow Reporter supports the following configuration options: | Option | Type | Default | Description | |--------|------|---------|-------------| | `apiKey` | string | required | Your QAFlow API key | -| `pingInterval` | number | `null` | Interval in milliseconds for sending ping requests | -| `autoScreenshot` | boolean | `false` | Whether to automatically capture screenshots | ## Verifying Installation @@ -106,7 +104,6 @@ reporter.createTest( // Add a test step await reporter.step("Verify connection", async () => { // This step should pass - return true; }); // End the test From 9a264aefcde118853ac1048078de223883141d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:23:32 +0100 Subject: [PATCH 02/31] refactor: update database schema --- prisma/schema.prisma | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5faf31a..fa8ed02 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,5 +1,6 @@ generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" + output = "./prisma-client" //the value to adjust or add if you dont have it } datasource db { From 1f19df49adb959cdc1a57caa5c83e1f50557ff57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:24:13 +0100 Subject: [PATCH 03/31] refactor: update logi and register pages --- src/app/(auth)/login/page.tsx | 32 +++++++++++++------------------- src/app/(auth)/register/page.tsx | 5 ++--- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index 866c113..b2d228b 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -8,9 +8,11 @@ import { useState } from "react"; import { signInAction } from "@/lib/actions"; import { Toaster, toast } from "react-hot-toast"; import { useRouter, useSearchParams } from "next/navigation"; +import { useSession } from "next-auth/react"; import { DEFAULT_LOGIN_REDIRECT } from "@/lib/routes"; export default function LoginPage() { + const session = useSession(); const [isSubmitting, setIsSubmitting] = useState(false); const router = useRouter(); const searchParams = useSearchParams(); @@ -18,28 +20,20 @@ export default function LoginPage() { const handleSubmit = async (data: SignInValues) => { setIsSubmitting(true); - try { - const result = await signInAction(data); - - if (result?.error) { - toast.error(result.error, { - position: "top-right", - duration: 3000, - }); - } else { - toast.success("Login successful!", { - position: "top-right", - duration: 3000, - }); - } - } catch (error) { - toast.error("An unexpected error occurred", { + const result = await signInAction(data); + if (result.success) { + await session.update(); + toast.success("Login successful!", { position: "top-right", duration: 3000, }); - } finally { - setIsSubmitting(false); + } else { + toast.error("Login failed. Please check your credentials."); } + + setTimeout(() => { + router.push(callbackUrl || DEFAULT_LOGIN_REDIRECT); + }, 1000); }; return ( @@ -69,7 +63,7 @@ export default function LoginPage() {
- Don't have an account? + Don't have an account? - Date: Wed, 5 Mar 2025 14:24:56 +0100 Subject: [PATCH 04/31] feat: add contact page --- src/app/(home)/contact/page.tsx | 229 ++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/app/(home)/contact/page.tsx diff --git a/src/app/(home)/contact/page.tsx b/src/app/(home)/contact/page.tsx new file mode 100644 index 0000000..2f410dc --- /dev/null +++ b/src/app/(home)/contact/page.tsx @@ -0,0 +1,229 @@ +"use client"; + +import { useState } from "react"; +import { FaEnvelope, FaMapMarkerAlt, FaPhone, FaSpinner, FaCheckCircle, FaExclamationCircle } from "react-icons/fa"; + +export default function ContactPage() { + const [formData, setFormData] = useState({ + name: "", + email: "", + subject: "", + message: "", + }); + + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitStatus, setSubmitStatus] = useState(null); + const [errorMessage, setErrorMessage] = useState(""); + + const handleChange = (e: React.ChangeEvent) => { + const { id, value } = e.target; + setFormData((prev) => ({ ...prev, [id]: value })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setIsSubmitting(true); + setSubmitStatus(null); + setErrorMessage(""); + + try { + const response = await fetch("/api/contact", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.message || "An error occurred."); + } + + setSubmitStatus("success"); + setFormData({ + name: "", + email: "", + subject: "", + message: "", + }); + } catch (error) { + setSubmitStatus("error"); + setErrorMessage(error instanceof Error ? error.message : "An error occurred."); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+
+
+

Get in Touch

+

+ We're here to help with any questions about our platform. + Reach out to us and we'll respond as soon as we can. +

+
+
+ +
+
+
+

Send Us a Message

+ + {submitStatus === "success" && ( +
+ +

Your message has been sent successfully! We'll get back to you shortly.

+
+ )} + + {submitStatus === "error" && ( +
+ +

{errorMessage || "There was an error sending your message. Please try again."}

+
+ )} + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+ +
+
+ +
+
+

Contact Information

+

+ Our team is available Monday through Friday from 9am to 5pm to assist you with any inquiries. +

+
+ +
+
+
+ +
+
+

Email

+

support@qaflow.com

+

info@qaflow.com

+
+
+ + {/* +
+
+ +
+
+

Phone

+

+1 (555) 123-4567

+

Mon-Fri 9am-5pm

+
+
+ */} + + {/* +
+
+ +
+
+

Location

+

123 Testing Street

+

San Francisco, CA 94103

+
+
+ */} +
+ +
+

Office Hours

+
+

+ Monday - Friday: + 9:00 AM - 5:00 PM +

+

+ Saturday: + Closed +

+

+ Sunday: + Closed +

+
+
+
+
+
+
+ ); +} \ No newline at end of file From f23dd29c2936603868448f198e1989d151bb95ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:26:16 +0100 Subject: [PATCH 05/31] feat: add cookies page --- src/app/(home)/cookies/page.tsx | 129 ++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/app/(home)/cookies/page.tsx diff --git a/src/app/(home)/cookies/page.tsx b/src/app/(home)/cookies/page.tsx new file mode 100644 index 0000000..09c4614 --- /dev/null +++ b/src/app/(home)/cookies/page.tsx @@ -0,0 +1,129 @@ +import { FaCookieBite } from "react-icons/fa"; + +export default function CookiePolicyPage() { + const lastUpdated = "June 15, 2023"; + + return ( +
+
+
+ +

Cookie Policy

+

+ Learn how and why we use cookies and similar technologies on our platform. +

+

Last Updated: {lastUpdated}

+
+
+ +
+
+
+

1. What Are Cookies?

+

+ Cookies are small pieces of text sent to your browser when you visit our website. They serve a variety of functions, like enabling us to remember certain information about your session and preferences, enhancing your browsing experience, and helping us to improve our service. +

+ +

2. Types of Cookies We Use

+

We use the following types of cookies on our website:

+ +

Essential Cookies

+

+ These cookies are necessary for the website to function properly. They enable core functionality such as security, network management, and account authentication. You may disable these by changing your browser settings, but this may affect how the website functions. +

+ +

Performance and Analytics Cookies

+

+ These cookies allow us to count visits and traffic sources so we can measure and improve the performance of our site. They help us to know which pages are the most and least popular and see how visitors move around the site. +

+ +

Functionality Cookies

+

+ These cookies enable the website to provide enhanced functionality and personalization. They may be set by us or by third-party providers whose services we have added to our pages. +

+ +

Targeting/Advertising Cookies

+

+ These cookies are used to deliver advertisements more relevant to you and your interests. They are also used to limit the number of times you see an advertisement as well as help measure the effectiveness of the advertising campaign. +

+ +

3. Cookie Management

+

+ Most web browsers allow some control of most cookies through the browser settings. To find out more about cookies, including how to see what cookies have been set, visit www.allaboutcookies.org. +

+

You can manage your cookie preferences in various ways, depending on your browser:

+
    +
  • Chrome: Settings > Privacy and security > Cookies and other site data
  • +
  • Firefox: Options > Privacy & Security > Cookies and Site Data
  • +
  • Safari: Preferences > Privacy > Cookies and website data
  • +
  • Edge: Settings > Cookies and site permissions > Cookies
  • +
+

+ Please note that restricting cookies may impact your experience on our website, as some features may not function properly. +

+ +

4. Third-Party Cookies

+

+ In addition to our own cookies, we may also use various third-party cookies to report usage statistics, deliver advertisements, and so on. These cookies may be placed when you visit our website or when you open emails from us, and are sometimes used to track when you have opened or clicked on a link in an email. +

+

+ Third-party providers we use may include Google Analytics, HubSpot, Intercom, and social media platforms for sharing and engagement. +

+ +

5. Specific Cookies We Use

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameProviderPurposeExpiry
session_idqaflow.comAuthentication and session managementSession
_gaGoogle AnalyticsUsage statistics2 years
_gidGoogle AnalyticsUsage statistics24 hours
preferenceqaflow.comUser preferences1 year
+ +

6. Changes to This Cookie Policy

+

+ We may update our Cookie Policy from time to time. We will notify you of any changes by posting the new Cookie Policy on this page and updating the "Last Updated" date at the top of this policy. +

+

+ We encourage you to review this Cookie Policy periodically to stay informed about our use of cookies. +

+ +

7. Contact Us

+

+ If you have any questions about our Cookie Policy, please contact us at: +

+

Email: support@qaflow.com

+
+
+
+
+ ); +} \ No newline at end of file From 38b6fd3c42f20ab2c5ba2ccafaa901331d279bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:26:27 +0100 Subject: [PATCH 06/31] feat: add faq page --- src/app/(home)/faq/page.tsx | 176 ++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 src/app/(home)/faq/page.tsx diff --git a/src/app/(home)/faq/page.tsx b/src/app/(home)/faq/page.tsx new file mode 100644 index 0000000..9274ff3 --- /dev/null +++ b/src/app/(home)/faq/page.tsx @@ -0,0 +1,176 @@ +"use client"; + +import { useState } from "react"; +import { FaChevronDown, FaChevronUp, FaSearch } from "react-icons/fa"; + +export default function FAQPage() { + const [openIndex, setOpenIndex] = useState(null); + const [searchQuery, setSearchQuery] = useState(""); + + const faqCategories = [ + { + title: "General Questions", + questions: [ + { + question: "What is QA Flow?", + answer: + "QA Flow is a next-generation test management platform designed for modern teams. It helps you organize, execute, and track your testing processes more efficiently, with powerful features for collaboration and reporting." + }, + { + question: "How do I get started with QA Flow?", + answer: + "Getting started is easy! Simply register for an account, verify your email, and you'll be guided through our onboarding process. You can import your existing test cases or start creating new ones right away." + }, + { + question: "Is there a free trial available?", + answer: + "Yes, we offer a 14-day free trial with full access to all features. No credit card is required to start your trial." + } + ] + }, + { + title: "Account & Billing", + questions: [ + { + question: "How do I change my password?", + answer: + "You can change your password by going to your Account Settings page. Look for the 'Security' tab, where you'll find the option to update your password." + }, + { + question: "What payment methods do you accept?", + answer: + "We accept all major credit cards (Visa, MasterCard, American Express) as well as PayPal. For enterprise plans, we also offer invoicing options." + }, + { + question: "Can I upgrade or downgrade my plan?", + answer: + "Yes, you can change your plan at any time. Changes take effect at the start of your next billing cycle. When upgrading, you'll get immediate access to additional features." + } + ] + }, + { + title: "Features & Functionality", + questions: [ + { + question: "Can I import test cases from other tools?", + answer: + "Yes, QA Flow supports importing test cases from various formats including CSV, Excel, and direct integrations with popular tools like Jira, TestRail, and more." + }, + { + question: "Does QA Flow support API testing?", + answer: + "Absolutely! QA Flow provides robust API testing capabilities, allowing you to define, execute, and monitor API tests from within the platform." + }, + { + question: "What kind of reports can I generate?", + answer: + "QA Flow offers comprehensive reporting with customizable dashboards. You can generate test execution reports, coverage reports, trend analysis, and more. All reports can be exported in various formats." + } + ] + } + ]; + + const filteredFAQs = searchQuery.trim() === "" + ? faqCategories + : faqCategories.map(category => ({ + ...category, + questions: category.questions.filter(item => + item.question.toLowerCase().includes(searchQuery.toLowerCase()) || + item.answer.toLowerCase().includes(searchQuery.toLowerCase()) + ) + })).filter(category => category.questions.length > 0); + + const Accordion = ({ question, answer, index, isOpen, toggleAccordion }: any) => ( +
+ + {isOpen && ( +
+

{answer}

+
+ )} +
+ ); + + return ( +
+
+
+

Frequently Asked Questions

+

+ Find answers to common questions about QA Flow. If you don't see what you're looking for, + feel free to contact our support team. +

+
+
+ +
+
+
+
+ +
+ setSearchQuery(e.target.value)} + /> +
+
+
+ +
+ {filteredFAQs.length > 0 ? ( + filteredFAQs.map((category, categoryIndex) => ( +
+

{category.title}

+
+ {category.questions.map((faq, index) => ( + + setOpenIndex(openIndex === `${categoryIndex}-${index}` ? null : `${categoryIndex}-${index}`) + } + /> + ))} +
+
+ )) + ) : ( +
+

No results found

+

+ Try adjusting your search or browse through our categories. +

+
+ )} + +
+

Still have questions?

+

+ If you couldn't find the answer you were looking for, our support team is here to help. +

+ + Contact Support + +
+
+
+ ); +} \ No newline at end of file From 9e8cee22815a081fd532cd884c9d9c209e1ffe16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:26:39 +0100 Subject: [PATCH 07/31] feat: add privacy page --- src/app/(home)/privacy/page.tsx | 124 ++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 src/app/(home)/privacy/page.tsx diff --git a/src/app/(home)/privacy/page.tsx b/src/app/(home)/privacy/page.tsx new file mode 100644 index 0000000..1826653 --- /dev/null +++ b/src/app/(home)/privacy/page.tsx @@ -0,0 +1,124 @@ +import { FaShieldAlt } from "react-icons/fa"; + +export default function PrivacyPolicyPage() { + const lastUpdated = "June 15, 2023"; + + return ( +
+
+
+ +

Privacy Policy

+

+ We respect your privacy and are committed to protecting your personal data. + This privacy policy explains how we collect, use, and safeguard your information. +

+

Last Updated: {lastUpdated}

+
+
+ +
+
+
+

1. Introduction

+

+ At QA Flow, we take your privacy seriously. This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you use our platform. Please read this privacy policy carefully. If you do not agree with the terms of this privacy policy, please do not access the site. +

+ +

2. Information We Collect

+

We collect information that you provide directly to us when you:

+
    +
  • Register for an account
  • +
  • Use our services
  • +
  • Participate in surveys or promotions
  • +
  • Contact customer support
  • +
  • Apply for a job
  • +
+ +

This information may include:

+
    +
  • Name and contact details
  • +
  • Billing information
  • +
  • User credentials
  • +
  • Profile information
  • +
  • Usage data and preferences
  • +
+ +

3. How We Use Your Information

+

We use the information we collect to:

+
    +
  • Provide, maintain, and improve our services
  • +
  • Process transactions and send related information
  • +
  • Send technical notices, updates, and support messages
  • +
  • Respond to your comments and questions
  • +
  • Develop new products and services
  • +
  • Monitor and analyze trends and usage
  • +
  • Detect, investigate, and prevent fraudulent transactions and other illegal activities
  • +
  • Personalize your experience
  • +
+ +

4. Cookies and Tracking Technologies

+

+ We use cookies and similar tracking technologies to track activity on our platform and hold certain information. Cookies are files with a small amount of data that may include an anonymous unique identifier. You can instruct your browser to refuse all cookies or to indicate when a cookie is being sent. +

+ +

5. Information Sharing and Disclosure

+

We may share your information in the following situations:

+
    +
  • With third-party service providers to facilitate our services
  • +
  • To comply with legal obligations
  • +
  • To protect and defend our rights and property
  • +
  • With business partners with your consent
  • +
  • With your consent or at your direction
  • +
+ +

6. Data Security

+

+ We have implemented appropriate technical and organizational security measures designed to protect the security of any personal information we process. However, please note that no electronic transmission or storage of information can be guaranteed to be 100% secure. +

+ +

7. Your Data Protection Rights

+

Depending on your location, you may have the following rights:

+
    +
  • Right to access the personal data we hold about you
  • +
  • Right to request correction of your personal data
  • +
  • Right to request deletion of your personal data
  • +
  • Right to object to processing of your personal data
  • +
  • Right to data portability
  • +
  • Right to withdraw consent
  • +
+ +

8. Children's Privacy

+

+ Our services are not intended for individuals under the age of 18. We do not knowingly collect personal data from children under 18. If we become aware that we have collected personal data from a child under 18, we will take steps to delete such information. +

+ +

9. Changes to This Privacy Policy

+

+ We may update our Privacy Policy from time to time. We will notify you of any changes by posting the new Privacy Policy on this page and updating the "Last Updated" date at the top of this policy. +

+ +

10. Contact Us

+

+ If you have any questions about this Privacy Policy, please contact us at: +

+

Email: support@qaflow.com

+
+
+ +
+

Have questions about our privacy policy?

+

+ Our team is here to help you understand how we protect your data. +

+ + Contact Us + +
+
+
+ ); +} \ No newline at end of file From dcdb00aa8b066ea17dae9d9f1fe8fd844c1ab058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:26:49 +0100 Subject: [PATCH 08/31] feat: add terms page --- src/app/(home)/terms/page.tsx | 123 ++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/app/(home)/terms/page.tsx diff --git a/src/app/(home)/terms/page.tsx b/src/app/(home)/terms/page.tsx new file mode 100644 index 0000000..819155f --- /dev/null +++ b/src/app/(home)/terms/page.tsx @@ -0,0 +1,123 @@ +import { FaFileContract } from "react-icons/fa"; + +export default function TermsOfServicePage() { + const lastUpdated = "June 15, 2023"; + + return ( +
+
+
+ +

Terms of Service

+

+ Please read these terms and conditions carefully before using our service. +

+

Last Updated: {lastUpdated}

+
+
+ +
+
+
+

1. Agreement to Terms

+

+ By accessing or using QA Flow's platform, you agree to be bound by these Terms of Service and all applicable laws and regulations. If you do not agree with any of these terms, you are prohibited from using or accessing this site. +

+ +

2. Use License

+

+ Permission is granted to temporarily use QA Flow's platform for personal, educational, or commercial purposes, subject to the following restrictions: +

+
    +
  • You must not modify or copy the materials outside of normal platform usage
  • +
  • You must not use the materials for any commercial purpose outside the scope of your subscription
  • +
  • You must not attempt to decompile or reverse engineer any software contained on QA Flow's platform
  • +
  • You must not remove any copyright or other proprietary notations from the materials
  • +
  • You must not transfer the materials to another person or "mirror" the materials on any other server
  • +
+ +

3. Accounts

+

+ When you create an account with us, you must provide accurate, complete, and up-to-date information. You are responsible for safeguarding the password that you use to access the service and for any activities or actions under your password. +

+

+ You agree not to disclose your password to any third party. You must notify us immediately upon becoming aware of any breach of security or unauthorized use of your account. +

+ +

4. Subscriptions

+

+ Some parts of the service are billed on a subscription basis. You will be billed in advance on a recurring basis, depending on the type of subscription plan you select. +

+

+ At the end of each billing period, your subscription will automatically renew under the same conditions unless you cancel it or QA Flow cancels it. You may cancel your subscription either through your online account management or by contacting our customer support team. +

+ +

5. Content

+

+ Our service allows you to post, link, store, share and otherwise make available certain information, text, graphics, videos, or other material. You are responsible for the content that you post to the service, including its legality, reliability, and appropriateness. +

+

+ By posting content to the service, you grant us the right to use, modify, publicly perform, publicly display, reproduce, and distribute such content on and through the service. You retain any and all of your rights to any content you submit, post or display on or through the service and you are responsible for protecting those rights. +

+ +

6. Prohibited Uses

+

You agree not to use the Service:

+
    +
  • In any way that violates any applicable federal, state, local, or international law or regulation
  • +
  • To transmit, or procure the sending of, any advertising or promotional material, including any "junk mail", "chain letter," "spam," or any other similar solicitation
  • +
  • To impersonate or attempt to impersonate QA Flow, a QA Flow employee, another user, or any other person or entity
  • +
  • To engage in any other conduct that restricts or inhibits anyone's use or enjoyment of the Service, or which may harm QA Flow or users of the Service
  • +
+ +

7. Intellectual Property

+

+ The Service and its original content (excluding content provided by users), features, and functionality are and will remain the exclusive property of QA Flow and its licensors. The Service is protected by copyright, trademark, and other laws of both the United States and foreign countries. +

+

+ Our trademarks and trade dress may not be used in connection with any product or service without the prior written consent of QA Flow. +

+ +

8. Termination

+

+ We may terminate or suspend your account immediately, without prior notice or liability, for any reason whatsoever, including without limitation if you breach the Terms. +

+

+ Upon termination, your right to use the Service will immediately cease. If you wish to terminate your account, you may simply discontinue using the Service or contact us to delete your account. +

+ +

9. Limitation of Liability

+

+ In no event shall QA Flow, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from your access to or use of or inability to access or use the Service. +

+ +

10. Changes to Terms

+

+ We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material we will try to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at our sole discretion. +

+ +

11. Contact Us

+

+ If you have any questions about these Terms, please contact us at: +

+

Email: legal@qaflow.com

+

Mail: QA Flow, 123 Testing Street, rancisco, CA 94103

+
+
+ + {/* Contact Box */} +
+

Questions about our terms?

+

+ If you have any questions about our terms of service, please don't hesitate to reach out. +

+ + Contact Us + +
+
+
+ ); +} \ No newline at end of file From ddd350cb7a39a1bedcd275c240b9d5ae54fd76dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:27:07 +0100 Subject: [PATCH 09/31] refactor: update home page --- src/app/(home)/home-layout.css | 11 ----------- src/app/(home)/layout.tsx | 26 +++++++++++++++----------- src/app/(home)/page.tsx | 11 ++++------- 3 files changed, 19 insertions(+), 29 deletions(-) delete mode 100644 src/app/(home)/home-layout.css diff --git a/src/app/(home)/home-layout.css b/src/app/(home)/home-layout.css deleted file mode 100644 index 89d6044..0000000 --- a/src/app/(home)/home-layout.css +++ /dev/null @@ -1,11 +0,0 @@ -@import "tailwindcss"; - -/* DaisyUI teması sadece home layout için */ -@plugin 'daisyui' { - themes: light --default; -} - -body { - font-family: Arial, Helvetica, sans-serif; - @apply text-black; -} \ No newline at end of file diff --git a/src/app/(home)/layout.tsx b/src/app/(home)/layout.tsx index 2d97155..5882afd 100644 --- a/src/app/(home)/layout.tsx +++ b/src/app/(home)/layout.tsx @@ -1,12 +1,16 @@ -import type { ReactNode } from 'react'; -import { HomeLayout } from 'fumadocs-ui/layouts/home'; -import { baseOptions } from '@/app/layout.config'; -import { Toaster } from 'react-hot-toast'; -import './home-layout.css'; +import Footer from "@/components/Footer"; +import Navbar from "@/components/Navbar"; +import React from "react"; -export default function Layout({ children }: Readonly<{ children: ReactNode }>) { - return <> - - {children} - ; -} +export default function HomeLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> + {children} +
+ + ); +} \ No newline at end of file diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index bf10f99..0a7d70d 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -1,11 +1,9 @@ "use client"; -import Footer from "@/components/Footer"; import Navbar from "@/components/Navbar"; import { motion } from "motion/react"; import Link from "next/link"; import { FaFlask, FaBug, FaChartLine, FaRocket, FaRegLightbulb, FaRegClock } from "react-icons/fa"; -import './home-layout.css'; function Home() { @@ -19,12 +17,12 @@ function Home() { visible: { opacity: 1, y: 0 }, }; - const MotionLink = motion(Link) + const MotionLink = motion.create(Link) return ( -
+
+
-
-
-
+
); } From 2e032d1cad0374e0a5e86b737469085b938fd0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:27:38 +0100 Subject: [PATCH 10/31] feat: add API routes for contact form --- src/app/api/contact/route.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/app/api/contact/route.ts diff --git a/src/app/api/contact/route.ts b/src/app/api/contact/route.ts new file mode 100644 index 0000000..fd4d627 --- /dev/null +++ b/src/app/api/contact/route.ts @@ -0,0 +1,28 @@ +import { NextResponse } from "next/server"; +import { sendContactEmailWithTemplate } from "@/lib/sendgrid"; + +export async function POST(req: Request) { + try { + const { name, email, subject, message } = await req.json(); + + if (!name || !email || !message) { + return NextResponse.json( + { error: "Name, email, and message fields are required." }, + { status: 400 } + ); + } + + await sendContactEmailWithTemplate(name, email, subject, message); + + return NextResponse.json( + { success: true, message: "Email sent successfully" }, + { status: 200 } + ); + } catch (error) { + console.error("Error sending email:", error); + return NextResponse.json( + { success: false, message: "An error occurred while sending the email" }, + { status: 500 } + ); + } +} \ No newline at end of file From 5d727149e890e1eb994b2ac1a43912083355e3f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:27:52 +0100 Subject: [PATCH 11/31] refactor: update API routes --- src/app/api/auth/[...nextauth]/route.ts | 4 ---- src/app/api/dashboard/stats/route.ts | 8 ++------ src/app/api/search/route.ts | 4 ---- src/app/api/tests/[id]/route.ts | 4 ---- src/app/api/tests/route.ts | 13 ++++++++----- src/app/api/user/token/regenerate/route.ts | 4 ---- src/app/api/user/token/route.ts | 4 ---- 7 files changed, 10 insertions(+), 31 deletions(-) diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 03ec61a..78960f0 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -1,7 +1,3 @@ import { handlers } from "@/auth" -export const config = { - runtime: "edge", -}; - export const { GET, POST } = handlers \ No newline at end of file diff --git a/src/app/api/dashboard/stats/route.ts b/src/app/api/dashboard/stats/route.ts index 01f7412..3a91ce3 100644 --- a/src/app/api/dashboard/stats/route.ts +++ b/src/app/api/dashboard/stats/route.ts @@ -2,10 +2,6 @@ import { NextRequest, NextResponse } from "next/server"; import prisma from "@/lib/prisma"; import { getUserIdFromSession } from "@/lib/auth-utils"; -export const config = { - runtime: "edge", -}; - export async function GET(req: NextRequest) { try { const { error, userId } = await getUserIdFromSession(req); @@ -44,8 +40,8 @@ export async function GET(req: NextRequest) { apiKeys } }); - } catch (error) { - console.error("Error fetching dashboard stats:", error); + } catch (_) { + console.error("Error fetching dashboard stats:", _); return NextResponse.json( { error: "An error occurred while fetching dashboard stats" }, { status: 500 } diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index ce10ab3..df88962 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -2,7 +2,3 @@ import { source } from '@/lib/source'; import { createFromSource } from 'fumadocs-core/search/server'; export const { GET } = createFromSource(source); - -export const config = { - runtime: "edge", -}; diff --git a/src/app/api/tests/[id]/route.ts b/src/app/api/tests/[id]/route.ts index b0f477c..ab575cd 100644 --- a/src/app/api/tests/[id]/route.ts +++ b/src/app/api/tests/[id]/route.ts @@ -2,10 +2,6 @@ import { NextRequest, NextResponse } from "next/server"; import prisma from "@/lib/prisma"; import { getUserIdFromSession } from "@/lib/auth-utils"; -export const config = { - runtime: "edge", -}; - export async function GET( req: NextRequest, { params }: { params: Promise<{ id: string }> } diff --git a/src/app/api/tests/route.ts b/src/app/api/tests/route.ts index ab2c8ec..9484a41 100644 --- a/src/app/api/tests/route.ts +++ b/src/app/api/tests/route.ts @@ -3,10 +3,6 @@ import prisma from "@/lib/prisma"; import { verifyApiToken } from "@/lib/utils"; import { getUserIdFromSession } from "@/lib/auth-utils"; -export const config = { - runtime: "edge", -}; - export async function GET(req: NextRequest) { try { const { error, userId } = await getUserIdFromSession(req); @@ -15,6 +11,13 @@ export async function GET(req: NextRequest) { return error; } + if (!userId) { + return NextResponse.json( + { error: "User not found" }, + { status: 401 } + ); + } + const reports = await prisma.testReport.findMany({ where: { userId: userId! }, orderBy: { createdAt: "desc" }, @@ -35,7 +38,6 @@ export async function GET(req: NextRequest) { return NextResponse.json({ reports }); } catch (error) { - console.error("Error fetching test reports:", error); return NextResponse.json( { error: "An error occurred while fetching test reports" }, { status: 500 } @@ -55,6 +57,7 @@ export async function POST(req: NextRequest) { } const apiToken = authHeader.substring(7); + console.log("apiToken", apiToken); const tokenData = await verifyApiToken(apiToken); diff --git a/src/app/api/user/token/regenerate/route.ts b/src/app/api/user/token/regenerate/route.ts index 367ad1e..5ee1ab8 100644 --- a/src/app/api/user/token/regenerate/route.ts +++ b/src/app/api/user/token/regenerate/route.ts @@ -2,10 +2,6 @@ import { NextRequest, NextResponse } from "next/server"; import { updateApiToken } from "@/lib/utils"; import { getUserIdFromSession } from "@/lib/auth-utils"; -export const config = { - runtime: "edge", -}; - export async function POST(req: NextRequest) { try { const { error, userId } = await getUserIdFromSession(req); diff --git a/src/app/api/user/token/route.ts b/src/app/api/user/token/route.ts index 8298850..2a5ab0b 100644 --- a/src/app/api/user/token/route.ts +++ b/src/app/api/user/token/route.ts @@ -3,10 +3,6 @@ import prisma from "@/lib/prisma"; import { generateApiToken } from "@/lib/utils"; import { getUserIdFromSession } from "@/lib/auth-utils"; -export const config = { - runtime: "edge", -}; - export async function GET(req: NextRequest) { try { const { error, userId } = await getUserIdFromSession(req); From 600243b49b485e20a4c84ef96a8debb94cec2d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:28:14 +0100 Subject: [PATCH 12/31] refactor: update dashboard page --- .../dashboard/_components/DashboardCard.tsx | 7 +- src/app/dashboard/_components/Navbar.tsx | 23 -- .../dashboard/_components/QuickActions.tsx | 26 +- .../dashboard/_components/RecentActivity.tsx | 24 +- src/app/dashboard/_components/Sidebar.tsx | 179 ++++++---- src/app/dashboard/layout.tsx | 29 +- src/app/dashboard/page.tsx | 35 +- src/app/dashboard/reports/[id]/page.tsx | 10 +- src/app/dashboard/reports/page.tsx | 154 +++++---- src/app/dashboard/token/page.tsx | 321 ++++++++++-------- 10 files changed, 434 insertions(+), 374 deletions(-) delete mode 100644 src/app/dashboard/_components/Navbar.tsx diff --git a/src/app/dashboard/_components/DashboardCard.tsx b/src/app/dashboard/_components/DashboardCard.tsx index d8634c7..c5ee858 100644 --- a/src/app/dashboard/_components/DashboardCard.tsx +++ b/src/app/dashboard/_components/DashboardCard.tsx @@ -2,7 +2,6 @@ import React from "react"; import { motion } from "motion/react"; -import { IconType } from "react-icons"; interface DashboardCardProps { title: string; @@ -37,13 +36,13 @@ export default function DashboardCard({ title, value, icon, color, delay = 0 }: animate={{ opacity: 1, y: 0 }} transition={{ delay }} whileHover={{ y: -5, transition: { delay: 0 } }} - className="card bg-white shadow-md hover:shadow-lg transition-all" + className="rounded-lg bg-white shadow-md hover:shadow-lg transition-all overflow-hidden" > -
+
{icon}
-

{value}

+

{value}

{title}

diff --git a/src/app/dashboard/_components/Navbar.tsx b/src/app/dashboard/_components/Navbar.tsx deleted file mode 100644 index eb7967f..0000000 --- a/src/app/dashboard/_components/Navbar.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client"; - -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import { useSession } from "next-auth/react"; -import { FaBell, FaSearch, FaQuestionCircle } from "react-icons/fa"; -import { motion } from "motion/react"; - -export default function Navbar() { - const { data: session } = useSession(); - - return ( -
-
- -
-
- ); -} \ No newline at end of file diff --git a/src/app/dashboard/_components/QuickActions.tsx b/src/app/dashboard/_components/QuickActions.tsx index 656c78b..2eb53d6 100644 --- a/src/app/dashboard/_components/QuickActions.tsx +++ b/src/app/dashboard/_components/QuickActions.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { motion } from "motion/react"; -import { FaKey, FaClipboardList, FaPlus, FaChartLine, FaInfoCircle } from "react-icons/fa"; +import { FaKey, FaClipboardList, FaInfoCircle } from "react-icons/fa"; export default function QuickActions() { return ( @@ -10,11 +10,11 @@ export default function QuickActions() { initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.5 }} - className="card bg-white shadow-md hover:shadow-lg transition-all" + className="rounded-lg bg-white shadow-md hover:shadow-lg transition-all overflow-hidden" > -
-

- +
+

+ Quick Actions

@@ -23,7 +23,7 @@ export default function QuickActions() { Manage API Key @@ -34,23 +34,13 @@ export default function QuickActions() { View Test Reports - - - - - Analytics - - +

diff --git a/src/app/dashboard/_components/RecentActivity.tsx b/src/app/dashboard/_components/RecentActivity.tsx index 87295a1..6ed5f90 100644 --- a/src/app/dashboard/_components/RecentActivity.tsx +++ b/src/app/dashboard/_components/RecentActivity.tsx @@ -19,11 +19,11 @@ export default function RecentActivity({ isLoading, totalTests, activities = [] const getStatusIcon = (status: string) => { switch (status.toLowerCase()) { case "passed": - return ; + return ; case "failed": - return ; + return ; default: - return ; + return ; } }; @@ -32,10 +32,10 @@ export default function RecentActivity({ isLoading, totalTests, activities = [] initial={{ opacity: 0, scale: 0.95 }} animate={{ opacity: 1, scale: 1 }} transition={{ delay: 0.6 }} - className="card bg-white shadow-md hover:shadow-lg transition-all h-full" + className="rounded-lg bg-white shadow-md hover:shadow-lg transition-all h-full overflow-hidden" > -
-

Recent Activity

+
+

Recent Activity

{isLoading ? (
@@ -58,8 +58,8 @@ export default function RecentActivity({ isLoading, totalTests, activities = [] className="flex items-center gap-3 p-3 rounded-xl hover:bg-gray-50 transition-colors" >
{getStatusIcon(activity.status)}
@@ -71,18 +71,18 @@ export default function RecentActivity({ isLoading, totalTests, activities = [] ))} -
+
- +
) : totalTests > 0 ? (

View your recent test reports in the Test Reports section.

-
+
- +
diff --git a/src/app/dashboard/_components/Sidebar.tsx b/src/app/dashboard/_components/Sidebar.tsx index 9f6bd1c..a4d19d6 100644 --- a/src/app/dashboard/_components/Sidebar.tsx +++ b/src/app/dashboard/_components/Sidebar.tsx @@ -2,25 +2,56 @@ import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; -import { useSession, signOut } from "next-auth/react"; import { FaHome, FaKey, FaClipboardList, - FaUser, - FaSignOutAlt + FaSignOutAlt, } from "react-icons/fa"; - -import { motion } from "motion/react"; +import { useState, useEffect, useCallback } from "react"; +import { signOut } from "next-auth/react"; export default function Sidebar() { const pathname = usePathname(); const router = useRouter(); + const [isOpen, setIsOpen] = useState(false); - const handleSignOut = async () => { - await signOut({ redirect: false }); - router.push("/login"); - }; + const toggleSidebar = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + setIsOpen(prevState => !prevState); + }, []); + + const closeSidebar = useCallback(() => { + setIsOpen(false); + }, []); + + useEffect(() => { + if (isOpen) { + closeSidebar(); + } + }, [pathname, isOpen, closeSidebar]); + + useEffect(() => { + const handleEsc = (e: KeyboardEvent) => { + if (e.key === 'Escape' && isOpen) { + closeSidebar(); + } + }; + + window.addEventListener('keydown', handleEsc); + return () => window.removeEventListener('keydown', handleEsc); + }, [isOpen, closeSidebar]); + + const handleSignOut = useCallback(async (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + try { + await signOut({ redirect: true, redirectTo: "/login" }); + } catch (error) { + console.error("Sign out error:", error); + } + }, [router]); const menuItems = [ { @@ -45,54 +76,88 @@ export default function Sidebar() { }; return ( -
- - -
-
- -
- QA Flow -
- -
- - - -
- - - Sign Out - -
- -
- QA Flow v1.0.0 -
-
-
+ + + + Sign Out + + +
+ QA Flow v1.0.0 +
+
+
+ + ); } \ No newline at end of file diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index 38dbf86..a42d795 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -5,7 +5,6 @@ import { useRouter } from "next/navigation"; import { Toaster } from "react-hot-toast"; import { useSession } from "next-auth/react"; import Sidebar from "./_components/Sidebar"; -import Navbar from "./_components/Navbar"; export default function DashboardLayout({ children, @@ -13,7 +12,7 @@ export default function DashboardLayout({ children: React.ReactNode; }) { const router = useRouter(); - const { status } = useSession(); + const { data: session, status } = useSession(); useEffect(() => { if (status === "unauthenticated") { @@ -24,29 +23,33 @@ export default function DashboardLayout({ if (status === "loading") { return (
-
+
+ Loading... +
); } if (status === "unauthenticated") { - return null; + return ( +
+
+ Loading... +
+
+ ); } return ( -
- +
+ -
- - -
+
+
{children} -
+
- -
); } \ No newline at end of file diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index e684b7d..503645d 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -2,7 +2,7 @@ import { useSession } from "next-auth/react"; import { useEffect, useState } from "react"; -import { FaKey, FaClipboardList, FaChartLine, FaCheckCircle, FaTimesCircle } from "react-icons/fa"; +import { FaKey, FaClipboardList, FaCheckCircle, FaTimesCircle } from "react-icons/fa"; import DashboardCard from "./_components/DashboardCard"; import QuickActions from "./_components/QuickActions"; @@ -85,7 +85,7 @@ export default function DashboardPage() { Welcome back, {session?.user?.username || "User"}!

- Here's an overview of your QA testing metrics and activities. + Here's an overview of your QA testing metrics and activities.

@@ -147,37 +147,6 @@ export default function DashboardPage() { activities={recentActivities} />
- -
-
-

- - Test Trends -

- - {isLoading ? ( -
-

Loading chart data...

-
- ) : stats.totalTests > 0 ? ( -
-
Advanced analytics coming soon to QA Flow
-
Stay tuned for visualizations and insights
-
Failure trend detection
-
Performance benchmarking
-
Release planned for next update
-
- ) : ( -
- -

No data to visualize yet

-

- Start running tests to see analytics and trends over time. -

-
- )} -
-
); } \ No newline at end of file diff --git a/src/app/dashboard/reports/[id]/page.tsx b/src/app/dashboard/reports/[id]/page.tsx index 2e648db..dd10afd 100644 --- a/src/app/dashboard/reports/[id]/page.tsx +++ b/src/app/dashboard/reports/[id]/page.tsx @@ -3,8 +3,9 @@ import { useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; import { motion } from "motion/react"; -import { Toaster, toast } from "react-hot-toast"; +import { toast } from "react-hot-toast"; import { FaArrowLeft, FaCheckCircle, FaTimesCircle, FaExclamationTriangle, FaClock, FaUser, FaDesktop, FaChrome, FaWindows } from "react-icons/fa"; +import Image from "next/image"; interface TestStep { name: string; @@ -198,7 +199,7 @@ export default function TestReportDetailPage() {
🔍

Report Not Found

-

The test report you're looking for doesn't exist or you don't have permission to view it.

+

The test report you're looking for doesn't exist or you don't have permission to view it.

-
+
+ {isLoading ? ( +
+
+

Loading your API key...

- -
- -
- + ) : apiToken ? ( + <> +
+ +
+ + +
+
+ +
+ +
+ + +
+

+ Use this value in the Authorization header when making API requests. +

+
+ +
-

- Use this value in the Authorization header when making API requests. -

-
- -
+ +
+
+
+ +
+
+

Warning

+
+

+ Regenerating will invalidate your current token and any systems using it will need to be updated. +

+
+
+
+
+ + ) : ( +
+
+ +
+

No API token found

+

Generate an API token to start using the QA Flow service

- -
- - - Regenerating will invalidate your current token and any systems using it will need to be updated. - -
- - ) : ( -
-

No API token found

- -
- )} + )} +
-
-
-
-
- +
+
+
+
+
+ +
+
+

Information

+

About using your API token

+
-

Information

-
-

- This API token is designed to be used with our npm module for test reporting. - The token provides secure access to the QA Flow API for submitting and retrieving test reports. -

- -
-
- -
-

Need help using this API token?

-

- See our comprehensive documentation for integration instructions, examples, and best practices. -

+
+
+

+ This API token is designed to be used with our npm module for test reporting. + The token provides secure access to the QA Flow API for submitting and retrieving test reports. +

+ +
+
+
+ +
+
+

Need help using this API token?

+
+

+ See our comprehensive documentation for integration instructions, examples, and best practices. +

+
+
+ + + + View API Documentation +
- - - - View API Documentation -
-
-
-
- +
+
+
+
+ +
+
+

Security Tips

+

Best practices for API token security

+
-

Security Tips

-
    -
  • -
    - 1 -
    -

    Keep your API token secure and never expose it in client-side code.

    -
  • -
  • -
    - 2 -
    -

    Use environment variables to store your token in your applications.

    -
  • -
  • -
    - 3 -
    -

    Regenerate your token if you suspect it has been compromised.

    -
  • -
  • -
    - 4 -
    -

    Always use HTTPS when making API requests with your token.

    -
  • -
+
+
    +
  • +
    1
    +

    Keep your API token secure and never expose it in client-side code.

    +
  • +
  • +
    2
    +

    Use environment variables to store your token in your applications.

    +
  • +
  • +
    3
    +

    Regenerate your token if you suspect it has been compromised.

    +
  • +
  • +
    4
    +

    Always use HTTPS when making API requests with your token.

    +
  • +
+
From 4a00e0958584358aea237d154b4f65c846583684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:28:43 +0100 Subject: [PATCH 13/31] refactor: update npm module documentation page --- src/app/docs/docs-layout.css | 3 ++- src/app/docs/layout.tsx | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/docs/docs-layout.css b/src/app/docs/docs-layout.css index ce4bd61..d520f21 100644 --- a/src/app/docs/docs-layout.css +++ b/src/app/docs/docs-layout.css @@ -1,5 +1,6 @@ @import "tailwindcss"; + @import 'fumadocs-ui/css/ocean.css'; @import 'fumadocs-ui/css/preset.css'; -@source '../../../node_modules/fumadocs-ui/dist/**/*.js'; +@source '../../../node_modules/fumadocs-ui/dist/**/*.js'; \ No newline at end of file diff --git a/src/app/docs/layout.tsx b/src/app/docs/layout.tsx index 7cb103b..79817c2 100644 --- a/src/app/docs/layout.tsx +++ b/src/app/docs/layout.tsx @@ -3,11 +3,14 @@ import type { ReactNode } from 'react'; import { baseOptions } from '@/app/layout.config'; import { source } from '@/lib/source'; import './docs-layout.css'; +import { RootProvider } from 'fumadocs-ui/provider'; export default function Layout({ children }: { children: ReactNode }) { return ( - - {children} - + + + {children} + + ); } From 473b6cf29160ddffe43c29a77d07c093f78a0fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:29:18 +0100 Subject: [PATCH 14/31] refactor: update default layouts --- src/app/layout.config.tsx | 4 ++-- src/app/layout.tsx | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/layout.config.tsx b/src/app/layout.config.tsx index 704fc5d..d7b7d7c 100644 --- a/src/app/layout.config.tsx +++ b/src/app/layout.config.tsx @@ -14,7 +14,7 @@ export const baseOptions: Partial = { nav: { title: ( <> - QA Flow - Report + QAFlow - Report ), }, @@ -31,7 +31,7 @@ export const baseOptions: Partial = { }, { text: 'GitHub', - url: 'https://github.com/dorukozgen/qaflow-web', + url: 'https://github.com/qa-flow', icon: } ] diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3b63ca2..457b7fa 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,10 +1,9 @@ import './globals.css' -import { RootProvider } from 'fumadocs-ui/provider'; import { Metadata } from 'next'; import { Inter } from 'next/font/google'; -import type { ReactNode } from 'react'; import NextAuthSessionProvider from "@/components/providers/SessionProvider"; import { auth } from "@/auth"; +import { Toaster } from 'react-hot-toast'; const inter = Inter({ subsets: ['latin'], @@ -25,8 +24,9 @@ export default async function RootLayout({ return ( + - {children} + {children} From f9668c271ed4411ffa9abb67c2e70dd091229b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doruk=20=C3=96zgen?= Date: Wed, 5 Mar 2025 14:29:37 +0100 Subject: [PATCH 15/31] refactor: update default components --- src/components/Footer.tsx | 17 +-- src/components/LoginForm.tsx | 48 ++++---- src/components/Navbar.tsx | 77 ++++++++++--- src/components/RegisterForm.tsx | 111 ++++++++----------- src/components/providers/SessionProvider.tsx | 4 +- 5 files changed, 143 insertions(+), 114 deletions(-) diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index cfd66da..fdccc8f 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -1,3 +1,5 @@ +"use client"; + import { motion } from "motion/react"; import { FaTwitter, FaLinkedin, FaInstagram, FaGithub } from "react-icons/fa"; import Link from "next/link"; @@ -9,8 +11,8 @@ export default function Footer() { title: "Quick Links", links: [ { label: "Home", href: "/" }, - { label: "Features", href: "/#features" }, - { label: "Pricing", href: "/pricing" }, + // { label: "Features", href: "/features" }, + // { label: "Pricing", href: "/pricing" }, { label: "Contact", href: "/contact" }, ], }, @@ -18,9 +20,9 @@ export default function Footer() { title: "Resources", links: [ { label: "Documentation", href: "/docs" }, - { label: "Support", href: "/support" }, - { label: "Blog", href: "/blog" }, - { label: "FAQs", href: "/faq" }, + // { label: "Support", href: "/support" }, + // { label: "Blog", href: "/blog" }, + { label: "FAQs", href: "/faq" }, ], }, { @@ -36,9 +38,9 @@ export default function Footer() { const socialLinks = [ { icon: , label: "Twitter", href: "https://twitter.com" }, - { icon: , label: "LinkedIn", href: "https://linkedin.com" }, + { icon: , label: "LinkedIn", href: "https://www.linkedin.com/company/qaflow-tech/" }, { icon: , label: "Instagram", href: "https://instagram.com" }, - { icon: , label: "GitHub", href: "https://github.com" } + { icon: , label: "GitHub", href: "https://github.com/qa-flow" } ]; return ( @@ -50,7 +52,6 @@ export default function Footer() { >
-

diff --git a/src/components/LoginForm.tsx b/src/components/LoginForm.tsx index 26c95cc..fc821fd 100644 --- a/src/components/LoginForm.tsx +++ b/src/components/LoginForm.tsx @@ -24,46 +24,42 @@ function LoginForm({ onSubmit, isSubmitting }: LoginFormProps) { return (
-
-