From 123b2076e81bd813002a79d36e6b468dc51ec6ce Mon Sep 17 00:00:00 2001
From: maanasb01 <maanasb01@gmail.com>
Date: Mon, 20 Jan 2025 18:38:16 +0530
Subject: [PATCH] create crowdsource context & add login in plugin

---
 browser-extension/plugin/.env.development     |   2 +-
 browser-extension/plugin/src/api.js           |  21 +++
 .../plugin/src/ui-components/pages/App.jsx    |   1 +
 .../plugin/src/ui-components/pages/Debug.jsx  | 129 +++++++++++++++---
 .../src/ui-components/pages/SlurCreate.jsx    |   2 +-
 .../lib/uli_community/user_contribution.ex    | 104 ++++++++++++++
 .../user_contribution/crowdsourced_slur.ex    |  23 ++++
 .../user_session_controller_api.ex            |   2 +-
 ...250120074709_create_crowdsourced_slurs.exs |  19 +++
 .../fixtures/user_contribution_fixtures.ex    |  26 ++++
 .../uli_community/user_contribution_test.exs  |  71 ++++++++++
 11 files changed, 375 insertions(+), 25 deletions(-)
 create mode 100644 browser-extension/plugin/src/api.js
 create mode 100644 uli-community/lib/uli_community/user_contribution.ex
 create mode 100644 uli-community/lib/uli_community/user_contribution/crowdsourced_slur.ex
 create mode 100644 uli-community/priv/repo/migrations/20250120074709_create_crowdsourced_slurs.exs
 create mode 100644 uli-community/test/support/fixtures/user_contribution_fixtures.ex
 create mode 100644 uli-community/test/uli_community/user_contribution_test.exs

diff --git a/browser-extension/plugin/.env.development b/browser-extension/plugin/.env.development
index a41444a5..845728b3 100644
--- a/browser-extension/plugin/.env.development
+++ b/browser-extension/plugin/.env.development
@@ -1,4 +1,4 @@
 ENVIRONMENT=development
 LOCAL_STORAGE_NAME=ogbvData
-API_URL=http://localhost:3000
+API_URL=http://localhost:4000
 MEDIA_URL=https://uli-media.tattle.co.in
\ No newline at end of file
diff --git a/browser-extension/plugin/src/api.js b/browser-extension/plugin/src/api.js
new file mode 100644
index 00000000..b4c43452
--- /dev/null
+++ b/browser-extension/plugin/src/api.js
@@ -0,0 +1,21 @@
+import axios from "axios";
+import config from "./config";
+
+const { API_URL } = config;
+
+export async function userLogin({email, password}){
+
+    console.log("INSIDE USERLOGIN: ")
+    try {
+        const response = await axios.post(`${API_URL}/api/auth/login`, {
+            email,
+            password
+        });
+        console.log("RESPONSE IS: ",response)
+        console.log('Login successful:', response.data);
+        return response.data; 
+    } catch (error) {
+        
+        throw error;
+    }
+}
diff --git a/browser-extension/plugin/src/ui-components/pages/App.jsx b/browser-extension/plugin/src/ui-components/pages/App.jsx
index 410c3521..1ee00b7d 100644
--- a/browser-extension/plugin/src/ui-components/pages/App.jsx
+++ b/browser-extension/plugin/src/ui-components/pages/App.jsx
@@ -46,6 +46,7 @@ export function App() {
         async function navigatePreferences() {
             try {
                 const userData = await getUserData();
+                console.log("USER DATA: ", userData)
                 const preferenceData = await getPreferenceData();
 
                 if (!ignore) {
diff --git a/browser-extension/plugin/src/ui-components/pages/Debug.jsx b/browser-extension/plugin/src/ui-components/pages/Debug.jsx
index 77ade592..d00902f2 100644
--- a/browser-extension/plugin/src/ui-components/pages/Debug.jsx
+++ b/browser-extension/plugin/src/ui-components/pages/Debug.jsx
@@ -9,7 +9,7 @@ import {
     TextInput,
     Heading
 } from 'grommet';
-import { UserContext } from '../atoms/AppContext';
+import { UserContext, NotificationContext } from '../atoms/AppContext';
 import repository from '../../repository';
 import config from '../../config';
 import { useTranslation } from 'react-i18next';
@@ -18,41 +18,126 @@ const { getUserData, getPreferenceData, setUserData, setPreferenceData } =
     repository;
 const { resetAccount } = Api;
 import { Hide, View } from 'grommet-icons';
+import { userLogin } from '../../api';
 
 export function Debug() {
     const { user, setUser } = useContext(UserContext);
+    const { showNotification } = useContext(NotificationContext);
 
     const [localStorageData, setLocalStorageData] = useState(undefined);
     const { t, i18n } = useTranslation();
+        const [isResetChecked, setIsResetChecked] = useState(false);
 
-    useEffect(() => {
-        async function localStorage() {
-            const userData = await getUserData();
-            const preferenceData = await getPreferenceData();
-            if (!ignore) {
-                setLocalStorageData({
-                    user: userData,
-                    preference: preferenceData
-                });
-            }
-        }
-        let ignore = false;
-        localStorage();
-        return () => {
-            ignore = true;
-        };
-    }, []);
-
-    return <Box>{user ? <Box>Logged in. Settings</Box> : <LoginForm />}</Box>;
+    // useEffect(() => {
+    //     async function localStorage() {
+    //         const userData = await getUserData();
+    //         const preferenceData = await getPreferenceData();
+    //         if (!ignore) {
+    //             setLocalStorageData({
+    //                 user: userData,
+    //                 preference: preferenceData
+    //             });
+    //         }
+    //     }
+    //     let ignore = false;
+    //     localStorage();
+    //     return () => {
+    //         ignore = true;
+    //     };
+    // }, []);
+
+    return (
+        <Box>
+            {user ? (
+                <Box>
+                    <Text>
+                        Hello, <b>{user?.email}</b> !
+                    </Text>
+                    <Box
+                        pad={'small'}
+                        border={{ color: 'status-critical' }}
+                        margin={{ top: 'xsmall' }}
+                        fill={'horizontal'}
+                        align="start"
+                    >
+                        <Text color={'status-critical'}>
+                            Logout
+                        </Text>
+                        <Box height={'0.8em'}></Box>
+                        <Box gap={'small'}>
+                            <CheckBox
+                                checked={isResetChecked}
+                                label={"I am sure I want to logout from this account"}
+                                onChange={(e) =>
+                                    setIsResetChecked(e.target.checked)
+                                }
+                            />
+                            <Button
+                                label={"Logout"}
+                                disabled={!isResetChecked}
+                                secondary
+                                onClick={async()=>{
+                                    await setUserData(undefined);
+                                    setUser(null);
+                                }}
+                            />
+                        </Box>
+                    </Box>
+                </Box>
+            ) : (
+                <LoginForm />
+            )}
+        </Box>
+    );
 }
 
 const LoginForm = () => {
     const [reveal, setReveal] = useState(false);
     const [formValues, setFormValues] = useState({ email: '', password: '' });
+    const { showNotification } = useContext(NotificationContext);
+    const { user, setUser } = useContext(UserContext);
 
-    const handleSubmit = ({ value }) => {
+    async function handleSubmit({ value }) {
         console.log('Form Submitted:', value);
-    };
+
+        try {
+            let data = await userLogin(value);
+
+            console.log('LOGIN SUCCESS: ', data);
+            showNotification({
+                type: 'info',
+                message: 'Login Successful'
+            });
+
+            const { email, token } = data;
+
+            let setData = { email, token };
+
+            await setUserData(setData);
+            setUser(setData);
+        } catch (error) {
+            console.error(error);
+            if (error?.response?.status === 401) {
+                // console.log('UNAUTHORIZED', error);
+                showNotification({
+                    type: 'error',
+                    message: 'Unauthorized'
+                });
+            } else if (error?.response?.status >= 500) {
+                // console.log('UNAUTHORIZED', error);
+                showNotification({
+                    type: 'error',
+                    message: 'Server Error'
+                });
+            } else {
+                // console.log('SOMETHING WENT WRONG', error);
+                showNotification({
+                    type: 'error',
+                    message: 'Something Went Wrong'
+                });
+            }
+        }
+    }
 
     return (
         <Box>
diff --git a/browser-extension/plugin/src/ui-components/pages/SlurCreate.jsx b/browser-extension/plugin/src/ui-components/pages/SlurCreate.jsx
index fb402d7e..68621214 100644
--- a/browser-extension/plugin/src/ui-components/pages/SlurCreate.jsx
+++ b/browser-extension/plugin/src/ui-components/pages/SlurCreate.jsx
@@ -36,7 +36,7 @@ export function SlurCreate() {
         let newValue = slurCreatePluginToApi(value);
         // console.log(newValue);
         try {
-            await createSlurAndCategory(user.accessToken, newValue);
+            await createSlurAndCategory("dea07e31-417c-4547-9208-57ff7fcf2da8", newValue);
             navigate('/slur');
             showNotification({
                 type: 'message',
diff --git a/uli-community/lib/uli_community/user_contribution.ex b/uli-community/lib/uli_community/user_contribution.ex
new file mode 100644
index 00000000..1a28bb84
--- /dev/null
+++ b/uli-community/lib/uli_community/user_contribution.ex
@@ -0,0 +1,104 @@
+defmodule UliCommunity.UserContribution do
+  @moduledoc """
+  The UserContribution context.
+  """
+
+  import Ecto.Query, warn: false
+  alias UliCommunity.Repo
+
+  alias UliCommunity.UserContribution.CrowdsourcedSlur
+
+  @doc """
+  Returns the list of crowdsourced_slurs.
+
+  ## Examples
+
+      iex> list_crowdsourced_slurs()
+      [%CrowdsourcedSlur{}, ...]
+
+  """
+  def list_crowdsourced_slurs do
+    Repo.all(CrowdsourcedSlur)
+  end
+
+  @doc """
+  Gets a single crowdsourced_slur.
+
+  Raises `Ecto.NoResultsError` if the Crowdsourced slur does not exist.
+
+  ## Examples
+
+      iex> get_crowdsourced_slur!(123)
+      %CrowdsourcedSlur{}
+
+      iex> get_crowdsourced_slur!(456)
+      ** (Ecto.NoResultsError)
+
+  """
+  def get_crowdsourced_slur!(id), do: Repo.get!(CrowdsourcedSlur, id)
+
+  @doc """
+  Creates a crowdsourced_slur.
+
+  ## Examples
+
+      iex> create_crowdsourced_slur(%{field: value})
+      {:ok, %CrowdsourcedSlur{}}
+
+      iex> create_crowdsourced_slur(%{field: bad_value})
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def create_crowdsourced_slur(attrs \\ %{}) do
+    %CrowdsourcedSlur{}
+    |> CrowdsourcedSlur.changeset(attrs)
+    |> Repo.insert()
+  end
+
+  @doc """
+  Updates a crowdsourced_slur.
+
+  ## Examples
+
+      iex> update_crowdsourced_slur(crowdsourced_slur, %{field: new_value})
+      {:ok, %CrowdsourcedSlur{}}
+
+      iex> update_crowdsourced_slur(crowdsourced_slur, %{field: bad_value})
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def update_crowdsourced_slur(%CrowdsourcedSlur{} = crowdsourced_slur, attrs) do
+    crowdsourced_slur
+    |> CrowdsourcedSlur.changeset(attrs)
+    |> Repo.update()
+  end
+
+  @doc """
+  Deletes a crowdsourced_slur.
+
+  ## Examples
+
+      iex> delete_crowdsourced_slur(crowdsourced_slur)
+      {:ok, %CrowdsourcedSlur{}}
+
+      iex> delete_crowdsourced_slur(crowdsourced_slur)
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def delete_crowdsourced_slur(%CrowdsourcedSlur{} = crowdsourced_slur) do
+    Repo.delete(crowdsourced_slur)
+  end
+
+  @doc """
+  Returns an `%Ecto.Changeset{}` for tracking crowdsourced_slur changes.
+
+  ## Examples
+
+      iex> change_crowdsourced_slur(crowdsourced_slur)
+      %Ecto.Changeset{data: %CrowdsourcedSlur{}}
+
+  """
+  def change_crowdsourced_slur(%CrowdsourcedSlur{} = crowdsourced_slur, attrs \\ %{}) do
+    CrowdsourcedSlur.changeset(crowdsourced_slur, attrs)
+  end
+end
diff --git a/uli-community/lib/uli_community/user_contribution/crowdsourced_slur.ex b/uli-community/lib/uli_community/user_contribution/crowdsourced_slur.ex
new file mode 100644
index 00000000..2c7ee87b
--- /dev/null
+++ b/uli-community/lib/uli_community/user_contribution/crowdsourced_slur.ex
@@ -0,0 +1,23 @@
+defmodule UliCommunity.UserContribution.CrowdsourcedSlur do
+  use Ecto.Schema
+  import Ecto.Changeset
+
+  schema "crowdsourced_slurs" do
+    field :label, :string
+    field :category, {:array, :string}
+    field :level_of_severity, Ecto.Enum, values: [:low, :medium, :high]
+    field :casual, :boolean, default: false
+    field :appropriated, :boolean, default: false
+    field :appropriation_context, :boolean, default: false
+    field :meaning, :string
+
+    timestamps(type: :utc_datetime)
+  end
+
+  @doc false
+  def changeset(crowdsourced_slur, attrs) do
+    crowdsourced_slur
+    |> cast(attrs, [:label, :level_of_severity, :casual, :appropriated, :appropriation_context, :meaning, :category])
+    |> validate_required([:label, :level_of_severity, :casual, :appropriated, :appropriation_context, :meaning, :category])
+  end
+end
diff --git a/uli-community/lib/uli_community_web/controllers/user_session_controller_api.ex b/uli-community/lib/uli_community_web/controllers/user_session_controller_api.ex
index 937428b0..eb495131 100644
--- a/uli-community/lib/uli_community_web/controllers/user_session_controller_api.ex
+++ b/uli-community/lib/uli_community_web/controllers/user_session_controller_api.ex
@@ -18,7 +18,7 @@ defmodule UliCommunityWeb.SessionControllerApi do
       user ->
         IO.inspect(user, label: "USER IS: ")
         with {:ok, token} <- Token.sign(%{user_id: user.id}) do
-          json(conn, %{token: token, message: "Token Generation Successful!"})
+          json(conn, %{token: token, message: "Token Generation Successful!", email: user.email})
         else
           _ ->
             conn
diff --git a/uli-community/priv/repo/migrations/20250120074709_create_crowdsourced_slurs.exs b/uli-community/priv/repo/migrations/20250120074709_create_crowdsourced_slurs.exs
new file mode 100644
index 00000000..509fdae5
--- /dev/null
+++ b/uli-community/priv/repo/migrations/20250120074709_create_crowdsourced_slurs.exs
@@ -0,0 +1,19 @@
+defmodule UliCommunity.Repo.Migrations.CreateCrowdsourcedSlurs do
+  use Ecto.Migration
+
+  def change do
+
+    execute("CREATE TYPE level_of_severity AS ENUM ('low', 'medium', 'high')")
+    create table(:crowdsourced_slurs) do
+      add :label, :string
+      add :level_of_severity, :level_of_severity
+      add :casual, :boolean, default: false, null: false
+      add :appropriated, :boolean, default: false, null: false
+      add :appropriation_context, :boolean, default: false, null: false
+      add :meaning, :text
+      add :category, {:array, :string}
+
+      timestamps(type: :utc_datetime)
+    end
+  end
+end
diff --git a/uli-community/test/support/fixtures/user_contribution_fixtures.ex b/uli-community/test/support/fixtures/user_contribution_fixtures.ex
new file mode 100644
index 00000000..70cb6933
--- /dev/null
+++ b/uli-community/test/support/fixtures/user_contribution_fixtures.ex
@@ -0,0 +1,26 @@
+defmodule UliCommunity.UserContributionFixtures do
+  @moduledoc """
+  This module defines test helpers for creating
+  entities via the `UliCommunity.UserContribution` context.
+  """
+
+  @doc """
+  Generate a crowdsourced_slur.
+  """
+  def crowdsourced_slur_fixture(attrs \\ %{}) do
+    {:ok, crowdsourced_slur} =
+      attrs
+      |> Enum.into(%{
+        appropriated: true,
+        appropriation_context: true,
+        casual: true,
+        category: ["option1", "option2"],
+        label: "some label",
+        level_of_severity: "some level_of_severity",
+        meaning: "some meaning"
+      })
+      |> UliCommunity.UserContribution.create_crowdsourced_slur()
+
+    crowdsourced_slur
+  end
+end
diff --git a/uli-community/test/uli_community/user_contribution_test.exs b/uli-community/test/uli_community/user_contribution_test.exs
new file mode 100644
index 00000000..83bbcdd4
--- /dev/null
+++ b/uli-community/test/uli_community/user_contribution_test.exs
@@ -0,0 +1,71 @@
+defmodule UliCommunity.UserContributionTest do
+  use UliCommunity.DataCase
+
+  alias UliCommunity.UserContribution
+
+  describe "crowdsourced_slurs" do
+    alias UliCommunity.UserContribution.CrowdsourcedSlur
+
+    import UliCommunity.UserContributionFixtures
+
+    @invalid_attrs %{label: nil, category: nil, level_of_severity: nil, casual: nil, appropriated: nil, appropriation_context: nil, meaning: nil}
+
+    test "list_crowdsourced_slurs/0 returns all crowdsourced_slurs" do
+      crowdsourced_slur = crowdsourced_slur_fixture()
+      assert UserContribution.list_crowdsourced_slurs() == [crowdsourced_slur]
+    end
+
+    test "get_crowdsourced_slur!/1 returns the crowdsourced_slur with given id" do
+      crowdsourced_slur = crowdsourced_slur_fixture()
+      assert UserContribution.get_crowdsourced_slur!(crowdsourced_slur.id) == crowdsourced_slur
+    end
+
+    test "create_crowdsourced_slur/1 with valid data creates a crowdsourced_slur" do
+      valid_attrs = %{label: "some label", category: ["option1", "option2"], level_of_severity: "some level_of_severity", casual: true, appropriated: true, appropriation_context: true, meaning: "some meaning"}
+
+      assert {:ok, %CrowdsourcedSlur{} = crowdsourced_slur} = UserContribution.create_crowdsourced_slur(valid_attrs)
+      assert crowdsourced_slur.label == "some label"
+      assert crowdsourced_slur.category == ["option1", "option2"]
+      assert crowdsourced_slur.level_of_severity == "some level_of_severity"
+      assert crowdsourced_slur.casual == true
+      assert crowdsourced_slur.appropriated == true
+      assert crowdsourced_slur.appropriation_context == true
+      assert crowdsourced_slur.meaning == "some meaning"
+    end
+
+    test "create_crowdsourced_slur/1 with invalid data returns error changeset" do
+      assert {:error, %Ecto.Changeset{}} = UserContribution.create_crowdsourced_slur(@invalid_attrs)
+    end
+
+    test "update_crowdsourced_slur/2 with valid data updates the crowdsourced_slur" do
+      crowdsourced_slur = crowdsourced_slur_fixture()
+      update_attrs = %{label: "some updated label", category: ["option1"], level_of_severity: "some updated level_of_severity", casual: false, appropriated: false, appropriation_context: false, meaning: "some updated meaning"}
+
+      assert {:ok, %CrowdsourcedSlur{} = crowdsourced_slur} = UserContribution.update_crowdsourced_slur(crowdsourced_slur, update_attrs)
+      assert crowdsourced_slur.label == "some updated label"
+      assert crowdsourced_slur.category == ["option1"]
+      assert crowdsourced_slur.level_of_severity == "some updated level_of_severity"
+      assert crowdsourced_slur.casual == false
+      assert crowdsourced_slur.appropriated == false
+      assert crowdsourced_slur.appropriation_context == false
+      assert crowdsourced_slur.meaning == "some updated meaning"
+    end
+
+    test "update_crowdsourced_slur/2 with invalid data returns error changeset" do
+      crowdsourced_slur = crowdsourced_slur_fixture()
+      assert {:error, %Ecto.Changeset{}} = UserContribution.update_crowdsourced_slur(crowdsourced_slur, @invalid_attrs)
+      assert crowdsourced_slur == UserContribution.get_crowdsourced_slur!(crowdsourced_slur.id)
+    end
+
+    test "delete_crowdsourced_slur/1 deletes the crowdsourced_slur" do
+      crowdsourced_slur = crowdsourced_slur_fixture()
+      assert {:ok, %CrowdsourcedSlur{}} = UserContribution.delete_crowdsourced_slur(crowdsourced_slur)
+      assert_raise Ecto.NoResultsError, fn -> UserContribution.get_crowdsourced_slur!(crowdsourced_slur.id) end
+    end
+
+    test "change_crowdsourced_slur/1 returns a crowdsourced_slur changeset" do
+      crowdsourced_slur = crowdsourced_slur_fixture()
+      assert %Ecto.Changeset{} = UserContribution.change_crowdsourced_slur(crowdsourced_slur)
+    end
+  end
+end