├── leadmanager ├── accounts │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ ├── admin.py │ ├── views.py │ ├── apps.py │ ├── urls.py │ ├── serializers.py │ └── api.py ├── frontend │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── src │ │ ├── index.js │ │ ├── components │ │ │ ├── leads │ │ │ │ ├── Dashboard.js │ │ │ │ ├── Leads.js │ │ │ │ └── Form.js │ │ │ ├── common │ │ │ │ └── PrivateRoute.js │ │ │ ├── layout │ │ │ │ ├── Alerts.js │ │ │ │ └── Header.js │ │ │ ├── App.js │ │ │ └── accounts │ │ │ │ ├── Login.js │ │ │ │ └── Register.js │ │ ├── reducers │ │ │ ├── index.js │ │ │ ├── messages.js │ │ │ ├── errors.js │ │ │ ├── leads.js │ │ │ └── auth.js │ │ ├── actions │ │ │ ├── messages.js │ │ │ ├── types.js │ │ │ ├── leads.js │ │ │ └── auth.js │ │ └── store.js │ ├── models.py │ ├── tests.py │ ├── admin.py │ ├── urls.py │ ├── apps.py │ ├── views.py │ └── templates │ │ └── frontend │ │ └── index.html ├── leads │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_lead_owner.py │ │ └── 0001_initial.py │ ├── tests.py │ ├── admin.py │ ├── views.py │ ├── apps.py │ ├── urls.py │ ├── serializers.py │ ├── models.py │ └── api.py ├── leadmanager │ ├── __init__.py │ ├── urls.py │ ├── wsgi.py │ └── settings.py └── manage.py ├── .babelrc ├── .prettierrc ├── webpack.config.js ├── Pipfile ├── README.md ├── .editorconfig ├── .eslintrc ├── package.json ├── .gitignore └── Pipfile.lock /leadmanager/accounts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/frontend/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/leads/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/leadmanager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/accounts/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/frontend/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/leads/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import App from './components/App'; 2 | -------------------------------------------------------------------------------- /leadmanager/leads/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /leadmanager/accounts/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /leadmanager/accounts/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /leadmanager/frontend/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /leadmanager/frontend/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /leadmanager/leads/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /leadmanager/leads/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /leadmanager/accounts/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /leadmanager/accounts/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /leadmanager/frontend/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"], 3 | "plugins": ["transform-class-properties"] 4 | } -------------------------------------------------------------------------------- /leadmanager/leads/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LeadsConfig(AppConfig): 5 | name = 'leads' 6 | -------------------------------------------------------------------------------- /leadmanager/frontend/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | urlpatterns = [ 4 | path('', views.index) 5 | ] -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "trailingComma": "all", 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": true 7 | } -------------------------------------------------------------------------------- /leadmanager/accounts/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountsConfig(AppConfig): 5 | name = 'accounts' 6 | -------------------------------------------------------------------------------- /leadmanager/frontend/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FrontendConfig(AppConfig): 5 | name = 'frontend' 6 | -------------------------------------------------------------------------------- /leadmanager/frontend/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | def index(request): 4 | return render(request, 'frontend/index.html') 5 | -------------------------------------------------------------------------------- /leadmanager/leads/urls.py: -------------------------------------------------------------------------------- 1 | from rest_framework import routers 2 | from .api import LeadViewSet 3 | 4 | router = routers.DefaultRouter() 5 | router.register('api/leads', LeadViewSet, 'leads') 6 | 7 | urlpatterns = router.urls -------------------------------------------------------------------------------- /leadmanager/leads/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from leads.models import Lead 3 | 4 | # Lead Serializer 5 | class LeadSerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Lead 8 | fields = '__all__' -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: { 3 | rules: [ 4 | { 5 | test: /\.js$/, 6 | exclude: /node_modules/, 7 | use: { 8 | loader: "babel-loader" 9 | } 10 | } 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /leadmanager/leadmanager/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path, include 3 | 4 | urlpatterns = [ 5 | path('', include('frontend.urls')), 6 | path('', include('leads.urls')), 7 | path('', include('accounts.urls')) 8 | ] 9 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | django = "*" 10 | djangorestframework = "*" 11 | django-rest-knox = "*" 12 | psycopg2 = "*" 13 | 14 | [requires] 15 | python_version = "3.8" 16 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/leads/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | import Form from './Form'; 3 | import Leads from './Leads'; 4 | 5 | export default function Dashboard() { 6 | return ( 7 | 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import leads from './leads'; 3 | import errors from './errors'; 4 | import messages from './messages'; 5 | import auth from './auth'; 6 | 7 | export default combineReducers({ 8 | leads, 9 | errors, 10 | messages, 11 | auth, 12 | }); 13 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/reducers/messages.js: -------------------------------------------------------------------------------- 1 | import { CREATE_MESSAGE } from '../actions/types'; 2 | 3 | const initialState = {}; 4 | 5 | export default function (state = initialState, action) { 6 | switch (action.type) { 7 | case CREATE_MESSAGE: 8 | return (state = action.payload); 9 | default: 10 | return state; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lead Manager 2 | 3 | > Full stack Django/React/Redux app that uses token based authentication with Knox. 4 | 5 | ## Quick Start 6 | 7 | ```bash 8 | # Install dependencies 9 | npm install 10 | 11 | # Serve API on localhost:8000 12 | python leadmanager/manage.py runserver 13 | 14 | # Run webpack (from root) 15 | npm run dev 16 | 17 | # Build for production 18 | npm run build 19 | ``` 20 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/actions/messages.js: -------------------------------------------------------------------------------- 1 | import { CREATE_MESSAGE, GET_ERRORS } from './types'; 2 | 3 | // CREATE MESSAGE 4 | export const createMessage = (msg) => { 5 | return { 6 | type: CREATE_MESSAGE, 7 | payload: msg, 8 | }; 9 | }; 10 | 11 | // RETURN ERRORS 12 | export const returnErrors = (msg, status) => { 13 | return { 14 | type: GET_ERRORS, 15 | payload: { msg, status }, 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/reducers/errors.js: -------------------------------------------------------------------------------- 1 | import { GET_ERRORS } from '../actions/types'; 2 | 3 | const initialState = { 4 | msg: {}, 5 | status: null, 6 | }; 7 | 8 | export default function (state = initialState, action) { 9 | switch (action.type) { 10 | case GET_ERRORS: 11 | return { 12 | msg: action.payload.msg, 13 | status: action.payload.status, 14 | }; 15 | default: 16 | return state; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /leadmanager/accounts/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from .api import RegisterAPI, LoginAPI, UserAPI 3 | from knox import views as knox_views 4 | 5 | urlpatterns = [ 6 | path('api/auth', include('knox.urls')), 7 | path('api/auth/register', RegisterAPI.as_view()), 8 | path('api/auth/login', LoginAPI.as_view()), 9 | path('api/auth/user', UserAPI.as_view()), 10 | path('api/auth/logout', knox_views.LogoutView.as_view(), name='knox_logout') 11 | ] -------------------------------------------------------------------------------- /leadmanager/frontend/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import { composeWithDevTools } from 'redux-devtools-extension'; 3 | import thunk from 'redux-thunk'; 4 | import rootReducer from './reducers'; 5 | 6 | const initialState = {}; 7 | 8 | const middleware = [thunk]; 9 | 10 | const store = createStore( 11 | rootReducer, 12 | initialState, 13 | composeWithDevTools(applyMiddleware(...middleware)), 14 | ); 15 | 16 | export default store; 17 | -------------------------------------------------------------------------------- /leadmanager/leads/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | 5 | class Lead(models.Model): 6 | name = models.CharField(max_length=100) 7 | email = models.EmailField(max_length=100, unique=True) 8 | message = models.CharField(max_length=500, blank=True) 9 | owner = models.ForeignKey( 10 | User, related_name="leads", on_delete=models.CASCADE, null=True) 11 | created_at = models.DateTimeField(auto_now_add=True) 12 | -------------------------------------------------------------------------------- /leadmanager/leadmanager/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for leadmanager project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'leadmanager.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | max_line_length = 100 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | max_line_length = 0 15 | trim_trailing_whitespace = false 16 | 17 | [{Makefile, **.mk}] 18 | # Use tabs for indentation (Makefiles require tabs) 19 | indent_style = tab 20 | 21 | [*.scss] 22 | indent_size = 2 23 | indent_style = space -------------------------------------------------------------------------------- /leadmanager/leads/api.py: -------------------------------------------------------------------------------- 1 | from leads.models import Lead 2 | from rest_framework import viewsets, permissions 3 | from .serializers import LeadSerializer 4 | 5 | # Lead Viewset 6 | 7 | 8 | class LeadViewSet(viewsets.ModelViewSet): 9 | permission_classes = [ 10 | permissions.IsAuthenticated, 11 | ] 12 | serializer_class = LeadSerializer 13 | 14 | def get_queryset(self): 15 | return self.request.user.leads.all() 16 | 17 | def perform_create(self, serializer): 18 | serializer.save(owner=self.request.user) 19 | -------------------------------------------------------------------------------- /leadmanager/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'leadmanager.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/actions/types.js: -------------------------------------------------------------------------------- 1 | export const GET_LEADS = 'GET_LEADS'; 2 | export const DELETE_LEAD = 'DELETE_LEAD'; 3 | export const ADD_LEAD = 'ADD_LEAD'; 4 | export const GET_ERRORS = 'GET_ERRORS'; 5 | export const CREATE_MESSAGE = 'CREATE_MESSAGE'; 6 | export const USER_LOADING = 'USER_LOADING'; 7 | export const USER_LOADED = 'USER_LOADED'; 8 | export const AUTH_ERROR = 'AUTH_ERROR'; 9 | export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; 10 | export const LOGIN_FAIL = 'LOGIN_FAIL'; 11 | export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; 12 | export const REGISTER_SUCCESS = 'REGISTER_SUCCESS'; 13 | export const REGISTER_FAIL = 'REGISTER_FAIL'; 14 | export const CLEAR_LEADS = 'CLEAR_LEADS'; 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "parserOptions": { 4 | "sourceType": "module", 5 | "allowImportExportEverywhere": false, 6 | "codeFrame": false 7 | }, 8 | "extends": [ 9 | "airbnb", 10 | "prettier" 11 | ], 12 | "env": { 13 | "browser": true, 14 | "jest": true 15 | }, 16 | "rules": { 17 | "max-len": [ 18 | "error", 19 | { 20 | "code": 100 21 | } 22 | ], 23 | "prefer-promise-reject-errors": [ 24 | "off" 25 | ], 26 | "react/jsx-filename-extension": [ 27 | "off" 28 | ], 29 | "react/prop-types": [ 30 | "warn" 31 | ], 32 | "no-return-assign": [ 33 | "off" 34 | ] 35 | } 36 | } -------------------------------------------------------------------------------- /leadmanager/leads/migrations/0002_lead_owner.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.5 on 2019-01-30 13:37 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ('leads', '0001_initial'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='lead', 18 | name='owner', 19 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='leads', to=settings.AUTH_USER_MODEL), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/common/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route, Redirect } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | 6 | const PrivateRoute = ({ component: Component, auth, ...rest }) => ( 7 | { 10 | if (auth.isLoading) { 11 | return Loading...; 12 | } else if (!auth.isAuthenticated) { 13 | return ; 14 | } else { 15 | return ; 16 | } 17 | }} 18 | /> 19 | ); 20 | 21 | const mapStateToProps = (state) => ({ 22 | auth: state.auth, 23 | }); 24 | 25 | export default connect(mapStateToProps)(PrivateRoute); 26 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/reducers/leads.js: -------------------------------------------------------------------------------- 1 | import { GET_LEADS, DELETE_LEAD, ADD_LEAD, CLEAR_LEADS } from '../actions/types.js'; 2 | 3 | const initialState = { 4 | leads: [], 5 | }; 6 | 7 | export default function (state = initialState, action) { 8 | switch (action.type) { 9 | case GET_LEADS: 10 | return { 11 | ...state, 12 | leads: action.payload, 13 | }; 14 | case DELETE_LEAD: 15 | return { 16 | ...state, 17 | leads: state.leads.filter((lead) => lead.id !== action.payload), 18 | }; 19 | case ADD_LEAD: 20 | return { 21 | ...state, 22 | leads: [...state.leads, action.payload], 23 | }; 24 | case CLEAR_LEADS: 25 | return { 26 | ...state, 27 | leads: [], 28 | }; 29 | default: 30 | return state; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /leadmanager/leads/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1.5 on 2019-01-28 22:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Lead', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=100)), 19 | ('email', models.EmailField(max_length=100, unique=True)), 20 | ('message', models.CharField(blank=True, max_length=500)), 21 | ('created_at', models.DateTimeField(auto_now_add=True)), 22 | ], 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /leadmanager/accounts/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from django.contrib.auth.models import User 3 | from django.contrib.auth import authenticate 4 | 5 | # User Serializer 6 | class UserSerializer(serializers.ModelSerializer): 7 | class Meta: 8 | model = User 9 | fields = ('id', 'username', 'email') 10 | 11 | # Register Serializer 12 | class RegisterSerializer(serializers.ModelSerializer): 13 | class Meta: 14 | model = User 15 | fields = ('id', 'username', 'email', 'password') 16 | extra_kwargs = {'password': {'write_only': True}} 17 | 18 | def create(self, validated_data): 19 | user = User.objects.create_user(validated_data['username'], validated_data['email'], validated_data['password']) 20 | 21 | return user 22 | 23 | # Login Serializer 24 | class LoginSerializer(serializers.Serializer): 25 | username = serializers.CharField() 26 | password = serializers.CharField() 27 | 28 | def validate(self, data): 29 | user = authenticate(**data) 30 | if user and user.is_active: 31 | return user 32 | raise serializers.ValidationError("Incorrect Credentials") -------------------------------------------------------------------------------- /leadmanager/frontend/templates/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Lead Manager 9 | 10 | 11 | 12 | {% load static %} 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/reducers/auth.js: -------------------------------------------------------------------------------- 1 | import { 2 | USER_LOADED, 3 | USER_LOADING, 4 | AUTH_ERROR, 5 | LOGIN_SUCCESS, 6 | LOGIN_FAIL, 7 | LOGOUT_SUCCESS, 8 | REGISTER_SUCCESS, 9 | REGISTER_FAIL, 10 | } from '../actions/types'; 11 | 12 | const initialState = { 13 | token: localStorage.getItem('token'), 14 | isAuthenticated: null, 15 | isLoading: false, 16 | user: null, 17 | }; 18 | 19 | export default function (state = initialState, action) { 20 | switch (action.type) { 21 | case USER_LOADING: 22 | return { 23 | ...state, 24 | isLoading: true, 25 | }; 26 | case USER_LOADED: 27 | return { 28 | ...state, 29 | isAuthenticated: true, 30 | isLoading: false, 31 | user: action.payload, 32 | }; 33 | case LOGIN_SUCCESS: 34 | case REGISTER_SUCCESS: 35 | localStorage.setItem('token', action.payload.token); 36 | return { 37 | ...state, 38 | ...action.payload, 39 | isAuthenticated: true, 40 | isLoading: false, 41 | }; 42 | case AUTH_ERROR: 43 | case LOGIN_FAIL: 44 | case LOGOUT_SUCCESS: 45 | case REGISTER_FAIL: 46 | localStorage.removeItem('token'); 47 | return { 48 | ...state, 49 | token: null, 50 | user: null, 51 | isAuthenticated: false, 52 | isLoading: false, 53 | }; 54 | default: 55 | return state; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/actions/leads.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { createMessage, returnErrors } from './messages'; 3 | import { tokenConfig } from './auth'; 4 | 5 | import { GET_LEADS, DELETE_LEAD, ADD_LEAD } from './types'; 6 | 7 | // GET LEADS 8 | export const getLeads = () => (dispatch, getState) => { 9 | axios 10 | .get('/api/leads/', tokenConfig(getState)) 11 | .then((res) => { 12 | dispatch({ 13 | type: GET_LEADS, 14 | payload: res.data, 15 | }); 16 | }) 17 | .catch((err) => dispatch(returnErrors(err.response.data, err.response.status))); 18 | }; 19 | 20 | // DELETE LEAD 21 | export const deleteLead = (id) => (dispatch, getState) => { 22 | axios 23 | .delete(`/api/leads/${id}/`, tokenConfig(getState)) 24 | .then((res) => { 25 | dispatch(createMessage({ deleteLead: 'Lead Deleted' })); 26 | dispatch({ 27 | type: DELETE_LEAD, 28 | payload: id, 29 | }); 30 | }) 31 | .catch((err) => console.log(err)); 32 | }; 33 | 34 | // ADD LEAD 35 | export const addLead = (lead) => (dispatch, getState) => { 36 | axios 37 | .post('/api/leads/', lead, tokenConfig(getState)) 38 | .then((res) => { 39 | dispatch(createMessage({ addLead: 'Lead Added' })); 40 | dispatch({ 41 | type: ADD_LEAD, 42 | payload: res.data, 43 | }); 44 | }) 45 | .catch((err) => dispatch(returnErrors(err.response.data, err.response.status))); 46 | }; 47 | -------------------------------------------------------------------------------- /leadmanager/accounts/api.py: -------------------------------------------------------------------------------- 1 | from rest_framework import generics, permissions 2 | from rest_framework.response import Response 3 | from knox.models import AuthToken 4 | from .serializers import UserSerializer, RegisterSerializer, LoginSerializer 5 | 6 | # Register API 7 | class RegisterAPI(generics.GenericAPIView): 8 | serializer_class = RegisterSerializer 9 | 10 | def post(self, request, *args, **kwargs): 11 | serializer = self.get_serializer(data=request.data) 12 | serializer.is_valid(raise_exception=True) 13 | user = serializer.save() 14 | return Response({ 15 | "user": UserSerializer(user, context=self.get_serializer_context()).data, 16 | "token": AuthToken.objects.create(user)[1] 17 | }) 18 | 19 | # Login API 20 | class LoginAPI(generics.GenericAPIView): 21 | serializer_class = LoginSerializer 22 | 23 | def post(self, request, *args, **kwargs): 24 | serializer = self.get_serializer(data=request.data) 25 | serializer.is_valid(raise_exception=True) 26 | user = serializer.validated_data 27 | _, token = AuthToken.objects.create(user) 28 | return Response({ 29 | "user": UserSerializer(user, context=self.get_serializer_context()).data, 30 | "token": token 31 | }) 32 | 33 | # Get User API 34 | class UserAPI(generics.RetrieveAPIView): 35 | permission_classes = [ 36 | permissions.IsAuthenticated, 37 | ] 38 | serializer_class = UserSerializer 39 | 40 | def get_object(self): 41 | return self.request.user 42 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/layout/Alerts.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | import { withAlert } from 'react-alert'; 3 | import { connect } from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | 6 | export class Alerts extends Component { 7 | static propTypes = { 8 | error: PropTypes.object.isRequired, 9 | message: PropTypes.object.isRequired, 10 | }; 11 | 12 | componentDidUpdate(prevProps) { 13 | const { error, alert, message } = this.props; 14 | if (error !== prevProps.error) { 15 | if (error.msg.name) alert.error(`Name: ${error.msg.name.join()}`); 16 | if (error.msg.email) alert.error(`Email: ${error.msg.email.join()}`); 17 | if (error.msg.message) alert.error(`Message: ${error.msg.message.join()}`); 18 | if (error.msg.non_field_errors) alert.error(error.msg.non_field_errors.join()); 19 | if (error.msg.username) alert.error(error.msg.username.join()); 20 | } 21 | 22 | if (message !== prevProps.message) { 23 | if (message.deleteLead) alert.success(message.deleteLead); 24 | if (message.addLead) alert.success(message.addLead); 25 | if (message.passwordNotMatch) alert.error(message.passwordNotMatch); 26 | } 27 | } 28 | 29 | render() { 30 | return ; 31 | } 32 | } 33 | 34 | const mapStateToProps = (state) => ({ 35 | error: state.errors, 36 | message: state.messages, 37 | }); 38 | 39 | export default connect(mapStateToProps)(withAlert()(Alerts)); 40 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; 4 | 5 | import { Provider as AlertProvider } from 'react-alert'; 6 | import AlertTemplate from 'react-alert-template-basic'; 7 | 8 | import Header from './layout/Header'; 9 | import Dashboard from './leads/Dashboard'; 10 | import Alerts from './layout/Alerts'; 11 | import Login from './accounts/Login'; 12 | import Register from './accounts/Register'; 13 | import PrivateRoute from './common/PrivateRoute'; 14 | 15 | import { Provider } from 'react-redux'; 16 | import store from '../store'; 17 | import { loadUser } from '../actions/auth'; 18 | 19 | // Alert Options 20 | const alertOptions = { 21 | timeout: 3000, 22 | position: 'top center', 23 | }; 24 | 25 | class App extends Component { 26 | componentDidMount() { 27 | store.dispatch(loadUser()); 28 | } 29 | 30 | render() { 31 | return ( 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | ); 50 | } 51 | } 52 | 53 | ReactDOM.render(, document.getElementById('app')); 54 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/leads/Leads.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Fragment } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import PropTypes from 'prop-types'; 4 | import { getLeads, deleteLead } from '../../actions/leads'; 5 | 6 | export class Leads extends Component { 7 | static propTypes = { 8 | leads: PropTypes.array.isRequired, 9 | getLeads: PropTypes.func.isRequired, 10 | deleteLead: PropTypes.func.isRequired, 11 | }; 12 | 13 | componentDidMount() { 14 | this.props.getLeads(); 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 | Leads 21 | 22 | 23 | 24 | ID 25 | Name 26 | Email 27 | Message 28 | 29 | 30 | 31 | 32 | {this.props.leads.map((lead) => ( 33 | 34 | {lead.id} 35 | {lead.name} 36 | {lead.email} 37 | {lead.message} 38 | 39 | 43 | {' '} 44 | Delete 45 | 46 | 47 | 48 | ))} 49 | 50 | 51 | 52 | ); 53 | } 54 | } 55 | 56 | const mapStateToProps = (state) => ({ 57 | leads: state.leads.leads, 58 | }); 59 | 60 | export default connect(mapStateToProps, { getLeads, deleteLead })(Leads); 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lead_manager_react_django", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack --mode development --watch ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js", 8 | "build": "webpack --mode production ./leadmanager/frontend/src/index.js --output ./leadmanager/frontend/static/frontend/main.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "axios": "^0.18.1", 15 | "prop-types": "^15.7.2", 16 | "react": "^16.13.1", 17 | "react-alert": "^7.0.0", 18 | "react-alert-template-basic": "^1.0.0", 19 | "react-dom": "^16.13.1", 20 | "react-redux": "^6.0.1", 21 | "react-router-dom": "^5.1.2", 22 | "react-transition-group": "^2.9.0", 23 | "redux": "^4.0.5", 24 | "redux-devtools-extension": "^2.13.8", 25 | "redux-thunk": "^2.3.0" 26 | }, 27 | "devDependencies": { 28 | "@babel/core": "^7.9.0", 29 | "@babel/preset-env": "^7.9.0", 30 | "@babel/preset-react": "^7.9.4", 31 | "babel-eslint": "^10.1.0", 32 | "babel-loader": "^8.1.0", 33 | "babel-plugin-transform-class-properties": "^6.24.1", 34 | "eslint": "^6.8.0", 35 | "eslint-config-airbnb": "^18.1.0", 36 | "eslint-config-prettier": "^6.10.1", 37 | "eslint-plugin-import": "^2.20.1", 38 | "eslint-plugin-jsx-a11y": "^6.2.3", 39 | "eslint-plugin-react": "^7.19.0", 40 | "husky": "^4.2.3", 41 | "prettier": "^2.0.2", 42 | "pretty-quick": "^2.0.1", 43 | "webpack": "^4.42.1", 44 | "webpack-cli": "^3.3.11" 45 | }, 46 | "husky": { 47 | "hooks": { 48 | "pre-commit": "lint-staged" 49 | } 50 | }, 51 | "lint-staged": { 52 | "src/**/*.js": [ 53 | "eslint", 54 | "pretty-quick — staged", 55 | "git add" 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/leads/Form.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import PropTypes from 'prop-types'; 4 | import { addLead } from '../../actions/leads'; 5 | 6 | export class Form extends Component { 7 | state = { 8 | name: '', 9 | email: '', 10 | message: '', 11 | }; 12 | 13 | static propTypes = { 14 | addLead: PropTypes.func.isRequired, 15 | }; 16 | 17 | onChange = (e) => this.setState({ [e.target.name]: e.target.value }); 18 | 19 | onSubmit = (e) => { 20 | e.preventDefault(); 21 | const { name, email, message } = this.state; 22 | const lead = { name, email, message }; 23 | this.props.addLead(lead); 24 | this.setState({ 25 | name: '', 26 | email: '', 27 | message: '', 28 | }); 29 | }; 30 | 31 | render() { 32 | const { name, email, message } = this.state; 33 | return ( 34 | 35 | Add Lead 36 | 37 | 38 | Name 39 | 46 | 47 | 48 | Email 49 | 56 | 57 | 58 | Message 59 | 66 | 67 | 68 | 69 | Submit 70 | 71 | 72 | 73 | 74 | ); 75 | } 76 | } 77 | 78 | export default connect(null, { addLead })(Form); 79 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/accounts/Login.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link, Redirect } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import { login } from '../../actions/auth'; 6 | 7 | export class Login extends Component { 8 | state = { 9 | username: '', 10 | password: '', 11 | }; 12 | 13 | static propTypes = { 14 | login: PropTypes.func.isRequired, 15 | isAuthenticated: PropTypes.bool, 16 | }; 17 | 18 | onSubmit = (e) => { 19 | e.preventDefault(); 20 | this.props.login(this.state.username, this.state.password); 21 | }; 22 | 23 | onChange = (e) => this.setState({ [e.target.name]: e.target.value }); 24 | 25 | render() { 26 | if (this.props.isAuthenticated) { 27 | return ; 28 | } 29 | const { username, password } = this.state; 30 | return ( 31 | 32 | 33 | Login 34 | 35 | 36 | Username 37 | 44 | 45 | 46 | 47 | Password 48 | 55 | 56 | 57 | 58 | 59 | Login 60 | 61 | 62 | 63 | Don't have an account? Register 64 | 65 | 66 | 67 | 68 | ); 69 | } 70 | } 71 | 72 | const mapStateToProps = (state) => ({ 73 | isAuthenticated: state.auth.isAuthenticated, 74 | }); 75 | 76 | export default connect(mapStateToProps, { login })(Login); 77 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/layout/Header.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import { logout } from '../../actions/auth'; 6 | 7 | export class Header extends Component { 8 | static propTypes = { 9 | auth: PropTypes.object.isRequired, 10 | logout: PropTypes.func.isRequired, 11 | }; 12 | 13 | render() { 14 | const { isAuthenticated, user } = this.props.auth; 15 | 16 | const authLinks = ( 17 | 18 | 19 | {user ? `Welcome ${user.username}` : ''} 20 | 21 | 22 | 23 | Logout 24 | 25 | 26 | 27 | ); 28 | 29 | const guestLinks = ( 30 | 31 | 32 | 33 | Register 34 | 35 | 36 | 37 | 38 | Login 39 | 40 | 41 | 42 | ); 43 | 44 | return ( 45 | 46 | 47 | 56 | 57 | 58 | 59 | 60 | Lead Manager 61 | 62 | 63 | {isAuthenticated ? authLinks : guestLinks} 64 | 65 | 66 | ); 67 | } 68 | } 69 | 70 | const mapStateToProps = (state) => ({ 71 | auth: state.auth, 72 | }); 73 | 74 | export default connect(mapStateToProps, { logout })(Header); 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | ### Django ### 4 | *.log 5 | *.pot 6 | *.pyc 7 | __pycache__/ 8 | local_settings.py 9 | db.sqlite3 10 | media 11 | 12 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 13 | # in your Git repository. Update and uncomment the following line accordingly. 14 | # /staticfiles/ 15 | 16 | ### Django.Python Stack ### 17 | # Byte-compiled / optimized / DLL files 18 | *.py[cod] 19 | *$py.class 20 | 21 | # C extensions 22 | *.so 23 | 24 | # Distribution / packaging 25 | .Python 26 | build/ 27 | develop-eggs/ 28 | dist/ 29 | downloads/ 30 | eggs/ 31 | .eggs/ 32 | lib/ 33 | lib64/ 34 | parts/ 35 | sdist/ 36 | var/ 37 | wheels/ 38 | share/python-wheels/ 39 | *.egg-info/ 40 | .installed.cfg 41 | *.egg 42 | MANIFEST 43 | 44 | # PyInstaller 45 | # Usually these files are written by a python script from a template 46 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 47 | *.manifest 48 | *.spec 49 | 50 | # Installer logs 51 | pip-log.txt 52 | pip-delete-this-directory.txt 53 | 54 | # Unit test / coverage reports 55 | htmlcov/ 56 | .tox/ 57 | .nox/ 58 | .coverage 59 | .coverage.* 60 | .cache 61 | nosetests.xml 62 | coverage.xml 63 | *.cover 64 | .hypothesis/ 65 | .pytest_cache/ 66 | 67 | # Translations 68 | *.mo 69 | 70 | # Django stuff: 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | .python-version 94 | 95 | # celery beat schedule file 96 | celerybeat-schedule 97 | 98 | # SageMath parsed files 99 | *.sage.py 100 | 101 | # Environments 102 | .env 103 | .venv 104 | env/ 105 | venv/ 106 | ENV/ 107 | env.bak/ 108 | venv.bak/ 109 | 110 | # Spyder project settings 111 | .spyderproject 112 | .spyproject 113 | 114 | # Rope project settings 115 | .ropeproject 116 | 117 | # mkdocs documentation 118 | /site 119 | 120 | # mypy 121 | .mypy_cache/ 122 | .dmypy.json 123 | dmypy.json 124 | 125 | # Pyre type checker 126 | .pyre/ 127 | 128 | .DS_STORE 129 | scripts/flow/*/.flowconfig 130 | *~ 131 | .grunt 132 | _SpecRunner.html 133 | __benchmarks__ 134 | remote-repo/ 135 | coverage/ 136 | .module-cache 137 | fixtures/dom/public/react-dom.js 138 | fixtures/dom/public/react.js 139 | test/the-files-to-test.generated.js 140 | *.log* 141 | chrome-user-data 142 | *.sublime-project 143 | *.sublime-workspace 144 | .idea 145 | *.iml 146 | .vscode 147 | *.swp 148 | *.swo 149 | /.pnp 150 | .pnp.js 151 | 152 | .env.local 153 | .env.development.local 154 | .env.test.local 155 | .env.production.local 156 | 157 | npm-debug.log* 158 | yarn-debug.log* 159 | yarn-error.log* 160 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/actions/auth.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { returnErrors } from './messages'; 3 | 4 | import { 5 | USER_LOADED, 6 | USER_LOADING, 7 | AUTH_ERROR, 8 | LOGIN_SUCCESS, 9 | LOGIN_FAIL, 10 | LOGOUT_SUCCESS, 11 | REGISTER_SUCCESS, 12 | REGISTER_FAIL, 13 | } from './types'; 14 | 15 | // CHECK TOKEN & LOAD USER 16 | export const loadUser = () => (dispatch, getState) => { 17 | // User Loading 18 | dispatch({ type: USER_LOADING }); 19 | 20 | axios 21 | .get('/api/auth/user', tokenConfig(getState)) 22 | .then((res) => { 23 | dispatch({ 24 | type: USER_LOADED, 25 | payload: res.data, 26 | }); 27 | }) 28 | .catch((err) => { 29 | dispatch(returnErrors(err.response.data, err.response.status)); 30 | dispatch({ 31 | type: AUTH_ERROR, 32 | }); 33 | }); 34 | }; 35 | 36 | // LOGIN USER 37 | export const login = (username, password) => (dispatch) => { 38 | // Headers 39 | const config = { 40 | headers: { 41 | 'Content-Type': 'application/json', 42 | }, 43 | }; 44 | 45 | // Request Body 46 | const body = JSON.stringify({ username, password }); 47 | 48 | axios 49 | .post('/api/auth/login', body, config) 50 | .then((res) => { 51 | dispatch({ 52 | type: LOGIN_SUCCESS, 53 | payload: res.data, 54 | }); 55 | }) 56 | .catch((err) => { 57 | dispatch(returnErrors(err.response.data, err.response.status)); 58 | dispatch({ 59 | type: LOGIN_FAIL, 60 | }); 61 | }); 62 | }; 63 | 64 | // REGISTER USER 65 | export const register = ({ username, password, email }) => (dispatch) => { 66 | // Headers 67 | const config = { 68 | headers: { 69 | 'Content-Type': 'application/json', 70 | }, 71 | }; 72 | 73 | // Request Body 74 | const body = JSON.stringify({ username, email, password }); 75 | 76 | axios 77 | .post('/api/auth/register', body, config) 78 | .then((res) => { 79 | dispatch({ 80 | type: REGISTER_SUCCESS, 81 | payload: res.data, 82 | }); 83 | }) 84 | .catch((err) => { 85 | dispatch(returnErrors(err.response.data, err.response.status)); 86 | dispatch({ 87 | type: REGISTER_FAIL, 88 | }); 89 | }); 90 | }; 91 | 92 | // LOGOUT USER 93 | export const logout = () => (dispatch, getState) => { 94 | axios 95 | .post('/api/auth/logout/', null, tokenConfig(getState)) 96 | .then((res) => { 97 | dispatch({ type: 'CLEAR_LEADS' }); 98 | dispatch({ 99 | type: LOGOUT_SUCCESS, 100 | }); 101 | }) 102 | .catch((err) => { 103 | dispatch(returnErrors(err.response.data, err.response.status)); 104 | }); 105 | }; 106 | 107 | // Setup config with token - helper function 108 | export const tokenConfig = (getState) => { 109 | // Get token from state 110 | const token = getState().auth.token; 111 | 112 | // Headers 113 | const config = { 114 | headers: { 115 | 'Content-Type': 'application/json', 116 | }, 117 | }; 118 | 119 | // If token, add to headers config 120 | if (token) { 121 | config.headers['Authorization'] = `Token ${token}`; 122 | } 123 | 124 | return config; 125 | }; 126 | -------------------------------------------------------------------------------- /leadmanager/frontend/src/components/accounts/Register.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link, Redirect } from 'react-router-dom'; 3 | import { connect } from 'react-redux'; 4 | import PropTypes from 'prop-types'; 5 | import { register } from '../../actions/auth'; 6 | import { createMessage } from '../../actions/messages'; 7 | 8 | export class Register extends Component { 9 | state = { 10 | username: '', 11 | email: '', 12 | password: '', 13 | password2: '', 14 | }; 15 | 16 | static propTypes = { 17 | register: PropTypes.func.isRequired, 18 | isAuthenticated: PropTypes.bool, 19 | }; 20 | 21 | onSubmit = (e) => { 22 | e.preventDefault(); 23 | const { username, email, password, password2 } = this.state; 24 | if (password !== password2) { 25 | this.props.createMessage({ passwordNotMatch: 'Passwords do not match' }); 26 | } else { 27 | const newUser = { 28 | username, 29 | password, 30 | email, 31 | }; 32 | this.props.register(newUser); 33 | } 34 | }; 35 | 36 | onChange = (e) => this.setState({ [e.target.name]: e.target.value }); 37 | 38 | render() { 39 | if (this.props.isAuthenticated) { 40 | return ; 41 | } 42 | const { username, email, password, password2 } = this.state; 43 | return ( 44 | 45 | 46 | Register 47 | 48 | 49 | Username 50 | 57 | 58 | 59 | Email 60 | 67 | 68 | 69 | Password 70 | 77 | 78 | 79 | Confirm Password 80 | 87 | 88 | 89 | 90 | Register 91 | 92 | 93 | 94 | Already have an account? Login 95 | 96 | 97 | 98 | 99 | ); 100 | } 101 | } 102 | 103 | const mapStateToProps = (state) => ({ 104 | isAuthenticated: state.auth.isAuthenticated, 105 | }); 106 | 107 | export default connect(mapStateToProps, { register, createMessage })(Register); 108 | -------------------------------------------------------------------------------- /leadmanager/leadmanager/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for leadmanager project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.5. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 't7g$gddxotz%5_gwrt64xf#-bjwr(bk_#10!ur9et_s-*vhqti' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | 'leads', 41 | 'rest_framework', 42 | 'frontend', 43 | 'knox', 44 | 'accounts', 45 | ] 46 | 47 | REST_FRAMEWORK = { 48 | 'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication',) 49 | } 50 | 51 | MIDDLEWARE = [ 52 | 'django.middleware.security.SecurityMiddleware', 53 | 'django.contrib.sessions.middleware.SessionMiddleware', 54 | 'django.middleware.common.CommonMiddleware', 55 | 'django.middleware.csrf.CsrfViewMiddleware', 56 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 57 | 'django.contrib.messages.middleware.MessageMiddleware', 58 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 59 | ] 60 | 61 | ROOT_URLCONF = 'leadmanager.urls' 62 | 63 | TEMPLATES = [ 64 | { 65 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 66 | 'DIRS': [], 67 | 'APP_DIRS': True, 68 | 'OPTIONS': { 69 | 'context_processors': [ 70 | 'django.template.context_processors.debug', 71 | 'django.template.context_processors.request', 72 | 'django.contrib.auth.context_processors.auth', 73 | 'django.contrib.messages.context_processors.messages', 74 | ], 75 | }, 76 | }, 77 | ] 78 | 79 | WSGI_APPLICATION = 'leadmanager.wsgi.application' 80 | 81 | 82 | # Database 83 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 84 | 85 | DATABASES = { 86 | 'default': { 87 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 88 | 'NAME': 'lead_manager_forked', 89 | 'USER': 'root', 90 | 'PASSWORD': 'admin123', 91 | 'HOST': 'localhost', 92 | 'PORT': '', 93 | } 94 | } 95 | 96 | # Password validation 97 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 98 | 99 | AUTH_PASSWORD_VALIDATORS = [ 100 | { 101 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 102 | }, 103 | { 104 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 105 | }, 106 | { 107 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 108 | }, 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 111 | }, 112 | ] 113 | 114 | 115 | # Internationalization 116 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 117 | 118 | LANGUAGE_CODE = 'en-us' 119 | 120 | TIME_ZONE = 'UTC' 121 | 122 | USE_I18N = True 123 | 124 | USE_L10N = True 125 | 126 | USE_TZ = True 127 | 128 | 129 | # Static files (CSS, JavaScript, Images) 130 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 131 | 132 | STATIC_URL = '/static/' 133 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "5477e1b3f85e9e8b4c13530def4e58170bb975212a1d692d4fe760615cc7f839" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "asgiref": { 20 | "hashes": [ 21 | "sha256:a5098bc870b80e7b872bff60bb363c7f2c2c89078759f6c47b53ff8c525a152e", 22 | "sha256:cd88907ecaec59d78e4ac00ea665b03e571cb37e3a0e37b3702af1a9e86c365a" 23 | ], 24 | "version": "==3.3.0" 25 | }, 26 | "cffi": { 27 | "hashes": [ 28 | "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", 29 | "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", 30 | "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", 31 | "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", 32 | "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", 33 | "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", 34 | "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", 35 | "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", 36 | "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", 37 | "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", 38 | "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", 39 | "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", 40 | "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", 41 | "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", 42 | "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", 43 | "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", 44 | "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", 45 | "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", 46 | "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", 47 | "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", 48 | "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", 49 | "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", 50 | "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", 51 | "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", 52 | "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", 53 | "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", 54 | "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", 55 | "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", 56 | "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", 57 | "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", 58 | "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", 59 | "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", 60 | "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", 61 | "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", 62 | "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", 63 | "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" 64 | ], 65 | "version": "==1.14.3" 66 | }, 67 | "cryptography": { 68 | "hashes": [ 69 | "sha256:22f8251f68953553af4f9c11ec5f191198bc96cff9f0ac5dd5ff94daede0ee6d", 70 | "sha256:284e275e3c099a80831f9898fb5c9559120d27675c3521278faba54e584a7832", 71 | "sha256:3e17d02941c0f169c5b877597ca8be895fca0e5e3eb882526a74aa4804380a98", 72 | "sha256:52a47e60953679eea0b4d490ca3c241fb1b166a7b161847ef4667dfd49e7699d", 73 | "sha256:57b8c1ed13b8aa386cabbfde3be175d7b155682470b0e259fecfe53850967f8a", 74 | "sha256:6a8f64ed096d13f92d1f601a92d9fd1f1025dc73a2ca1ced46dcf5e0d4930943", 75 | "sha256:6e8a3c7c45101a7eeee93102500e1b08f2307c717ff553fcb3c1127efc9b6917", 76 | "sha256:7ef41304bf978f33cfb6f43ca13bb0faac0c99cda33693aa20ad4f5e34e8cb8f", 77 | "sha256:87c2fffd61e934bc0e2c927c3764c20b22d7f5f7f812ee1a477de4c89b044ca6", 78 | "sha256:88069392cd9a1e68d2cfd5c3a2b0d72a44ef3b24b8977a4f7956e9e3c4c9477a", 79 | "sha256:8a0866891326d3badb17c5fd3e02c926b635e8923fa271b4813cd4d972a57ff3", 80 | "sha256:8f0fd8b0751d75c4483c534b209e39e918f0d14232c0d8a2a76e687f64ced831", 81 | "sha256:9a07e6d255053674506091d63ab4270a119e9fc83462c7ab1dbcb495b76307af", 82 | "sha256:9a8580c9afcdcddabbd064c0a74f337af74ff4529cdf3a12fa2e9782d677a2e5", 83 | "sha256:bd80bc156d3729b38cb227a5a76532aef693b7ac9e395eea8063ee50ceed46a5", 84 | "sha256:d1cbc3426e6150583b22b517ef3720036d7e3152d428c864ff0f3fcad2b97591", 85 | "sha256:e15ac84dcdb89f92424cbaca4b0b34e211e7ce3ee7b0ec0e4f3c55cee65fae5a", 86 | "sha256:e4789b84f8dedf190148441f7c5bfe7244782d9cbb194a36e17b91e7d3e1cca9", 87 | "sha256:f01c9116bfb3ad2831e125a73dcd957d173d6ddca7701528eff1e7d97972872c", 88 | "sha256:f0e3986f6cce007216b23c490f093f35ce2068f3c244051e559f647f6731b7ae", 89 | "sha256:f2aa3f8ba9e2e3fd49bd3de743b976ab192fbf0eb0348cebde5d2a9de0090a9f", 90 | "sha256:fb70a4cedd69dc52396ee114416a3656e011fb0311fca55eb55c7be6ed9c8aef" 91 | ], 92 | "index": "pypi", 93 | "version": "==3.2" 94 | }, 95 | "django": { 96 | "hashes": [ 97 | "sha256:642d8eceab321ca743ae71e0f985ff8fdca59f07aab3a9fb362c617d23e33a76", 98 | "sha256:d4666c2edefa38c5ede0ec1655424c56dc47ceb04b6d8d62a7eac09db89545c1" 99 | ], 100 | "index": "pypi", 101 | "version": "==3.0.5" 102 | }, 103 | "django-rest-knox": { 104 | "hashes": [ 105 | "sha256:4a57b05b04fcccc41fcc969ad0e3f180467d7045b45003b58f5a437d6d3370d4", 106 | "sha256:e4ac93e6d7dd63af5b58ff19d7f197788a874e932464d77db5630f185365eac0", 107 | "sha256:f7dac2f7a6ece7c1bd331bdb01dc3d16e0b340f3d1001a7285cc13b171d539a3" 108 | ], 109 | "index": "pypi", 110 | "version": "==4.1.0" 111 | }, 112 | "djangorestframework": { 113 | "hashes": [ 114 | "sha256:05809fc66e1c997fd9a32ea5730d9f4ba28b109b9da71fccfa5ff241201fd0a4", 115 | "sha256:e782087823c47a26826ee5b6fa0c542968219263fb3976ec3c31edab23a4001f" 116 | ], 117 | "index": "pypi", 118 | "version": "==3.11.0" 119 | }, 120 | "psycopg2": { 121 | "hashes": [ 122 | "sha256:4212ca404c4445dc5746c0d68db27d2cbfb87b523fe233dc84ecd24062e35677", 123 | "sha256:47fc642bf6f427805daf52d6e52619fe0637648fe27017062d898f3bf891419d", 124 | "sha256:72772181d9bad1fa349792a1e7384dde56742c14af2b9986013eb94a240f005b", 125 | "sha256:8396be6e5ff844282d4d49b81631772f80dabae5658d432202faf101f5283b7c", 126 | "sha256:893c11064b347b24ecdd277a094413e1954f8a4e8cdaf7ffbe7ca3db87c103f0", 127 | "sha256:92a07dfd4d7c325dd177548c4134052d4842222833576c8391aab6f74038fc3f", 128 | "sha256:965c4c93e33e6984d8031f74e51227bd755376a9df6993774fd5b6fb3288b1f4", 129 | "sha256:9ab75e0b2820880ae24b7136c4d230383e07db014456a476d096591172569c38", 130 | "sha256:b0845e3bdd4aa18dc2f9b6fb78fbd3d9d371ad167fd6d1b7ad01c0a6cdad4fc6", 131 | "sha256:dca2d7203f0dfce8ea4b3efd668f8ea65cd2b35112638e488a4c12594015f67b", 132 | "sha256:ed686e5926929887e2c7ae0a700e32c6129abb798b4ad2b846e933de21508151", 133 | "sha256:ef6df7e14698e79c59c7ee7cf94cd62e5b869db369ed4b1b8f7b729ea825712a", 134 | "sha256:f898e5cc0a662a9e12bde6f931263a1bbd350cfb18e1d5336a12927851825bb6" 135 | ], 136 | "index": "pypi", 137 | "version": "==2.8.4" 138 | }, 139 | "pycparser": { 140 | "hashes": [ 141 | "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", 142 | "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" 143 | ], 144 | "version": "==2.20" 145 | }, 146 | "pytz": { 147 | "hashes": [ 148 | "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", 149 | "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" 150 | ], 151 | "version": "==2020.1" 152 | }, 153 | "six": { 154 | "hashes": [ 155 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", 156 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" 157 | ], 158 | "version": "==1.15.0" 159 | }, 160 | "sqlparse": { 161 | "hashes": [ 162 | "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", 163 | "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" 164 | ], 165 | "version": "==0.4.1" 166 | } 167 | }, 168 | "develop": {} 169 | } 170 | --------------------------------------------------------------------------------
63 | Don't have an account? Register 64 |
94 | Already have an account? Login 95 |