<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { wardrobeActions, authActions } from '../actions';
import NavigationBar from './NavigationBar';

import 'react-multi-carousel/lib/styles.css';

// Slick carousel
import Slider from 'react-slick';
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { SampleNextArrow, SamplePrevArrow } from './SliderArrows';

import '../styles/style.scss';

// Resources
import closeticon from '../resources/closet.png';
// import maincloseticon from '../resources/closet2.png';
var domain = 'http://closetmap.herokuapp.com';
/* Component that represents the 'home page' user lands on after logging in */
class HomePage extends React.Component {

  componentDidMount() {
    this.props.fetchWardrobes();
    console.log(this.props.wardrobes)
  }

  render() {
    const sliderSettings = {
      dots: false,
      slidesToShow: 4,
      slidesToScroll: 4,
      initialSlide: 0,
      className: 'slides',
      infinite: false,
      nextArrow: <SampleNextArrow />,
      prevArrow: <SamplePrevArrow />,
      responsive: [
        {
          breakpoint: 1024,
          settings: {
            slidesToShow: 3,
            slidesToScroll: 3,
          }
        },
        {
          breakpoint: 600,
          settings: {
            slidesToShow: 2,
            slidesToScroll: 2,
            initialSlide: 2
          }
        },
        {
          breakpoint: 480,
          settings: {
            slidesToShow: 1,
            slidesToScroll: 1
          }
        }
      ]
    };

    return (
      <div className="home-container">
        <h1>Welcome to Your Wardrobes</h1>
        <p> Hint: Scroll to see your wardrobes and click on the buttons below to add a wardrobe or an item. The Main wardrobe contains all your clothes.
          </p>
        <Slider {...sliderSettings}>
          {console.log('WARDROBES', this.props.wardrobes)}
          {this.props.wardrobes.map(function (wrd) {
            let imgsrc = wrd.image ? (domain + wrd.image) : closeticon
            return (
              <div className="carousel-wardrobe" key={wrd.pk}>
                <h2>{wrd.name}</h2>
                <Link key={wrd.pk} to={{
                  pathname: `/wrdbs/${wrd.pk}`,
                  state: {
                    name: wrd.name
                  }
                }}>
                  <img alt={wrd.name} src={imgsrc} />
                </Link>
              </div>
            )
          })}
        </Slider>
      </div>
    )
  }
}

// "Map" application state to the props of the component    <li><Link to="/del-wrdb">Delete wardrobe</Link> </li>
const mapStateToProps = state => {
  console.log('STATE', state)
  return {
    wardrobes: state.wardrobes.wardrobes,
    user: state.auth.user
  }
}

// Mapping action dispatcher functions to props
const mapDispatchToProps = dispatch => {
  return {
    fetchWardrobes: () => {
      dispatch(wardrobeActions.fetchWardrobes());
    },
    logout: () => dispatch(authActions.logout())
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
<file_sep>var domain = 'http://closetmap.herokuapp.com';

// TODO: Handling failing requests
export const fetchWardrobeCategories = (wardrobeName) => {
  return (dispatch, getState) => {
    return fetch(domain + '/my_cats/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ curr_wd: wardrobeName })
    })
      .then(res => res.json())
      .then(categories => {
        console.log('Wardrobe categories', categories) // TODO Remove
        return dispatch({
          type: 'FETCH_CATEGORIES',
          categories
        })
      })
  }
}

// TODO: Handling failing requests
export const fetchAllCategories = () => {
  return (dispatch, getState) => {
    fetch(domain + '/all_cats/', {
      headers: {
        Authorization: `JWT ${getState().auth.token}`
      }
    })
      .then(res => res.json())
      .then(categories => {
        console.log(categories) // TODO Remove
        return dispatch({
          type: 'FETCH_ALL_CATEGORIES',
          categories
        })
      })
  }
}

// TODO: Handle failing request
export const addCategory = (data) => {
  return (dispatch, getState) => {

    let formData = new FormData();
    formData.append('category', data.category)

    if (data.image) {
      formData.append('image', data.image, data.image.name)
    } else {
      formData.append('image', data.image)
    }

    return fetch(domain + '/add_cat/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`
      },
      mode: 'cors',
      body: formData
    })
      .then(res => res.json())
      .then(category => {
        console.log('Added category', category)
        return dispatch({
          type: 'ADD_CATEGORY',
          category
        })
      })
  }
}

export const modifyCategory = (e, data) => {
  e.preventDefault()

  return (dispatch, getState) => {
    return fetch(domain + '/mod_cat/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
      .then(res => res.json())
      .then(json => {
        console.log('Modied category response: ', json)
        // Response doesn't currently contain info about updated category
        // Hence: do not dispatch an event maybe?
        // TODO: Dispatch event
      })
  }
}

export const deleteCategory = (e, data) => {
  e.preventDefault()

  return (dispatch, getState) => {
    return fetch(domain + '/del_cat/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
      .then(res => res.json())
      .then(json => {
        console.log('Delete category', json)
        // TODO: Dispatch event?
      })
  }
}
<file_sep>import { history } from '../router/history';

var domain = 'http://closetmap.herokuapp.com';

export const fetchWardrobes = () => {
  return (dispatch, getState) => {
    let headers = { "Content-Type": "application/json" };
    let token = getState().auth.token;

    if (token) {
      headers["Authorization"] = `JWT ${token}`;
    }

    return fetch(domain + '/my/', { headers, })
      .then(res => res.json())
      .then(wardrobes => {
        console.log(wardrobes) // TODO Remove
        return dispatch({
          type: 'FETCH_WARDROBES',
          wardrobes
        })
      })
  }
}

export const addWardrobe = (data) => {
  return (dispatch, getState) => {
    let formData = new FormData()
    formData.append('wardrobe', data.wardrobe)

    if (data.image) {
      formData.append('image', data.image, data.image.name)
    } else {
      formData.append('image', data.image)
    }

    return fetch(domain + '/add_wd/', {
      headers: {
        Authorization: `JWT ${getState().auth.token}`
      },
      method: 'POST',
      body: formData
    })
      .then(res => res.json())
      .then(wardrobe => {
        console.log('Added wardrobe', wardrobe) // TODO Remove
        dispatch({
          type: 'ADD_WARDROBE',
          wardrobe
        })
        history.push(`/wrdbs/${wardrobe.pk}`, { name: wardrobe.name })
        return wardrobe;
      })
  }
}

export const deleteWardrobe = (wardrobeName) => {
  return (dispatch, getState) => {

    return fetch(domain + '/del_wd/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ current_wd: wardrobeName })
    })
      .then(res => res.json())
      .then(json => {
        console.log('Delete wardrobe response', json)
        // TODO: Update redux aka dispatch event
        // TODO Redirect
        history.push('/home')
      })
  }
}

export const modifyWardrobe = (wardrobeName, newName) => {
  return (dispatch, getState) => {
    return fetch(domain + '/mod_wd/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        current_wd: wardrobeName,
        new_name: newName
      })
    })
      .then(res => res.json())
      .then(wardrobe => {
        console.log('Modified wardrobe', wardrobe)
        // TODO: dispatch an action?
      })
  }
}
<file_sep>import * as authActions from './auth';
import * as categoryActions from './categories';
import * as itemActions from './items';
import * as outfitActions from './outfits';
import * as wardrobeActions from './wardrobes';

export {
  authActions,
  categoryActions,
  itemActions,
  outfitActions,
  wardrobeActions
}<file_sep>import React from 'react';
import { Link } from 'react-router-dom';

/* Bootstrap */
import Container from 'react-bootstrap/Container';

import LoggedOutNav from './LoggedOutNav';

class Welcome extends React.Component {
    render() {
        return (
            <div>
                <LoggedOutNav />
                <Container>
                    <Link className="back-link" to="/">Back</Link>
                    <h1>Welcome  </h1>
                    <p> * </p>
                    <h3>To find you clothes with ability,</h3>
                    <h3>Remember to use your creativity,</h3>
                    <h3>The closet map is your servant,</h3>
                    <h3>To help you find your garment. </h3>
                    *<br /> ** <br /> *
                    <p> Whether you are a guy or a chick </p>
                    <p>Whether you be peaceful or rough</p>
                    <p>Whether you're young, whether you're old</p>
                    <p>Whether you are an atheist, Whether you are pious</p>
                    <p>Whether you're one-armed, Whether you're left-handed</p>
                    <p>Whether you're rich or still broke</p>
                    <p>Whether you're stressed or never in a hurry</p>
                    <p>Whether you're a cop, Whether you got busted</p>
                    <p>Whether you're white, Whether you're black</p>
                    <p>Whether you are on time or always late</p>
                    <p>Whether you're drunk, Whether you're round</p>
                    <p>Whether you're bald, whether you're blond</p>
                    <p>Whether you're a quail or a bobo</p>
                    <p>Whether you're gay or a virgin</p>
                    <p>Whether you're maniacal or messy</p>
                    <p>Whether you're still sober or alcoholic</p>
                    <p>Whether you're a dwarf or a perch</p>
                    <p>Whether you're two of you' or you're always fishing</p>
                    <p>Whether you're shy or a chatterbox</p>
                    <p>Whether you're a boss or unemployed</p>
                    <p>Whether you're a sportsman or a comic book smoker</p>
                    <p>Whether you've been in school for a long time, Whether you don't have a degree</p>
                    <p>Whether you like to be at peace or you like to party</p>
                    <p> *  </p>

                    <h2>The Closet Map can help you daily.</h2>
                </Container>
            </div>
        )
    }
}

export default Welcome;<file_sep>from django.contrib.auth.models import User
from django import forms
from django.db import models
from .models import *

class UserForm(forms.ModelForm):
    email = forms.EmailField(max_length=100, help_text='Required. Please enter a valid email address.')
    password = forms.CharField(widget=forms.PasswordInput)
    class Meta:
        model = User
        fields = ('username', 'email', 'password')
<file_sep>import { history } from '../router/history';

var domain = 'http://closetmap.herokuapp.com';

export const addItem = (data) => {
  return (dispatch, getState) => {
    let form_data = new FormData();
    form_data.append('name', data.name);
    form_data.append('user', getState().auth.user.username);
    form_data.append('wardrobe', data.wardrobe);
    form_data.append('category', data.category);

    if (data.image) {
      form_data.append('image', data.image, data.image.name);
    } else {
      form_data.append('image', data.image)
    }

    return fetch(domain + '/add_item/', {
      headers: {
        Authorization: `JWT ${getState().auth.token}`
      },
      body: form_data,
      method: 'POST',
      mode: 'cors'
    })
      .then(res => res.json())
      .then(item => {
        console.log(item)
        dispatch({
          type: 'ADD_ITEM',
          item: item
        })
        history.push(`/items/${item.pk}`, { item: item })
        return item;
      })
      .catch((err) => {
        throw err;
      })
  }
}

export const deleteItem = (item) => {
  return (dispatch, getState) => {
    return fetch(domain + '/del_item/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ name: item.name })
    })
      .then(res => res.json())
      .then(response => {
        // TODO: Would be better to redirect where the item was from
        history.push('/home')

        // Item: { username: username}
        /* return dispatch({
          type: 'DELETE_ITEM',
          item: item
        })*/
      })
      .catch((err) => {
        throw err;
      })
  }
}

export const modifyItem = (data) => {
  return (dispatch, getState) => {
    let form_data = new FormData();
    form_data.append('name', data.name);
    form_data.append('new_name', data.new_name);
    form_data.append('wardrobe', data.wardrobe);
    if (data.image) {
      form_data.append('image', data.image, data.image.name);
    }
    else {
      form_data.append('image', data.image);
    }
    form_data.append('category', data.category);

    return fetch(domain + '/mod_item/', {
      headers: {
        Authorization: `JWT ${getState().auth.token}`
      },
      body: form_data,
      method: 'POST',
      mode: 'cors'
    })
      .then(res => res.json())
      .then(item => {
        console.log('Modified item', item)
        return dispatch({
          type: 'MODIFY_ITEM',
          item: item
        })
      })
      .catch((err) => {
        throw err;
      })
  }
}

/**
 * This returns items in the wardrobe that don't belong to any category.
 */
export const fetchWardrobeItems = (wardrobe) => {
  return (dispatch, getState) => {
    let data = { wardrobe: wardrobe, category: '' };

    return fetch(domain + '/get_items/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
      .then(res => res.json())
      .then(items => {
        console.log('Fetch wardrobe items', items)
        return dispatch({
          type: 'FETCH_ITEMS',
          items: items
        })
      })
      .catch((err) => {
        throw err;
      })
  }
}

export const fetchWardrobeCategoryItems = (wardrobe, category) => {
  return (dispatch, getState) => {
    let data = { wardrobe: wardrobe, category: category }

    return fetch(domain + '/get_items/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${getState().auth.token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
      .then(res => res.json())
      .then(items => {
        console.log('Fetch wardrobe category items', items)
        return dispatch({
          type: 'FETCH_ITEMS_CATEGORY',
          items: items
        })
      })
      .catch((err) => {
        throw err;
      })
  }
}

export const fetchAllItems = () => {
  return (dispatch, getState) => {
    return fetch(domain + '/all_items/', {
      method: 'GET',
      headers: {
        Authorization: `JWT ${getState().auth.token}`
      },
    })
      .then(res => res.json())
      .then(items => {
        console.log('Fetch all items', items)
        return dispatch({
          type: 'FETCH_ALL_ITEMS',
          items: items
        })
      })
      .catch((err) => {
        throw err;
      })
  }
}
<file_sep>const initialState = {
  categories: []
}

export default function categories(state = initialState, action) {
  switch (action.type) {
    case 'FETCH_CATEGORIES':
    case 'FETCH_ALL_CATEGORIES':
      return {
        ...state,
        categories: [...action.categories]
      }
    case 'ADD_CATEGORY':
      return {
        ...state,
        categories: [...state.categories, action.category]
      }
    case 'DELETE_CATEGORY':
      return {
        // TODO: Implement
      }
    case 'MODIFY_CATEGORY':
      return {
        // TODO: Implement
      }
    default:
      return state
  }
}
<file_sep># WWW-Services-2019
Project for the 2019 instance of the course Design of WWW Services
<file_sep>// Initial state for wardrobes
// (initial state can be any valid javascript type and I've just used an array as a placeholder here)
const initialState = {
  wardrobes: []
};

export default function wardrobes(state = initialState, action) {
  switch (action.type) {
    case 'FETCH_WARDROBES':
      return {
        ...state,
        wardrobes: [...action.wardrobes]
      };
    case 'ADD_WARDROBE':
      return {
        ...state,
        wardrobes: [...state.wardrobes, action.wardrobe]
      };
    default:
      return state;
  }
}<file_sep>import React, { Component } from 'react';

/* Router */
import { Route, Switch, Router, Redirect } from 'react-router-dom';
import { history } from './router/history';

/* Redux */
import { Provider, connect } from "react-redux";
import rootReducer from './reducers';

import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import { authActions } from './actions';

/* Components */
import NotFound from './components/NotFound';
import LandingPage from './components/LandingPage';
import LoginForm from './components/LoginForm';
import SignupForm from './components/SignupForm';
import ItemForm from './components/ItemForm';
import ItemDelForm from './components/ItemDelForm';
import ItemPage from './components/ItemPage';
import CategoryPage from './components/CategoryPage';
import CategoryForm from './components/CategoryForm';
import HomePage from './components/HomePage';
import OutfitForm from './components/OutfitForm';
import Outfits from './components/Outfits';
import OutfitPage from './components/OutfitPage';
import Wardrobe from './components/Wardrobe';
import WardrobeForm from './components/WardrobeForm';
import WardrobeRenameForm from './components/WardrobeRenameForm';
import Welcome from './components/Welcome';
import FAQ from './components/FAQ';
import NavigationBar from './components/NavigationBar';

/* Styles */
import './styles/style.scss';

/* FontAwesome */
import { library } from '@fortawesome/fontawesome-svg-core';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { faTrash, faFolder, faTshirt, faWalking } from '@fortawesome/free-solid-svg-icons';

library.add(fab, faTrash, faFolder, faTshirt, faWalking);

var domain = 'http://closetmap.herokuapp.com';

// Create a redux store using the combined reducer from reducers/index.js
const reduxStore = createStore(
  rootReducer, applyMiddleware(thunk)
);

class AppContainerComp extends Component {

  componentDidMount() {
    // TODO: Check whether user is logged in, before trying to load user
    this.props.loadUser();
  }

  PrivateRoute = ({ component: ChildComponent, ...rest }) => {
    return <Route {...rest} render={props => {
      if (this.props.auth.isLoading) {
        return <em>Loading...</em>;
      } else if (!this.props.auth.isAuthenticated) {
        return <Redirect to="/login" />;
      } else {
        return (
          <div>
            <NavigationBar />
            <ChildComponent {...props} />
          </div>
        )
      }
    }} />
  }

  render() {
    let { PrivateRoute } = this;
    return (
      <Router history={history}>
        <Switch>
          <Route exact path="/" component={LandingPage} />
          <Route exact path="/login" component={LoginForm} />
          <Route exact path="/signup" component={SignupForm} />
          <Route exact path="/welcome" component={Welcome} />
          <Route exact path="/FAQ" component={FAQ} />
          <PrivateRoute exact path="/home" component={HomePage} />
          <PrivateRoute exact path="/addwrdb" component={WardrobeForm} />
          <PrivateRoute exact path="/addcat" component={CategoryForm} />
          <PrivateRoute exact path="/additem" component={ItemForm} />
          <PrivateRoute exact path="/wrdbs/:wardrobeId" component={Wardrobe} />
          <PrivateRoute exact path="/wrdbs/:wardbrobeId/:category" component={CategoryPage} />
          <PrivateRoute exact path="/items/:itemId" component={ItemPage} />
          <PrivateRoute exact path="/del-item" component={ItemDelForm} />
          <PrivateRoute exact path="/edit-wrdb" component={WardrobeRenameForm} />
          <PrivateRoute exact path="/outfits" component={Outfits} />
          <PrivateRoute exact path="/outfit/:outfitId" component={OutfitPage} />
          <PrivateRoute exact path="/addoutfit" component={OutfitForm} />
          <Route component={NotFound} />
        </Switch>
      </ Router >
    )
  }
}

const mapStateToProps = state => {
  return {
    auth: state.auth,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    loadUser: () => {
      return dispatch(authActions.loadUser());
    }
  }
}

let AppContainer = connect(mapStateToProps, mapDispatchToProps)(AppContainerComp);

class App extends Component {

  render() {
    return (
      <Provider store={reduxStore}>
        <AppContainer />
      </Provider>
    );
  }

  // TODO: Implement in actions
  wd_view = (e) => {
    e.preventDefault();
    var war = e.target.value;
    this.get_cats(war);
    if (war) {
      let data = { wardrobe: war, category: '' };
      var items = [];
      var items_str = [];
      fetch(domain + '/get_items/', {
        method: 'POST',
        headers: {
          Authorization: `JWT ${localStorage.getItem('token')}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
      })
        .then(res => res.json())
        .then(json => {
          //handle items here
          var url = '';
          for (var i = 0; i < json.length; i++) {
            url = (json[i].image).substring(16);
            items.push(<div key={i}><img src={url} alt={json[i].name} name={json[i].name} width="100" height="100" /><figcaption>{json[i].name}</figcaption></div>);
            items_str.push(json[i].name);
          }
          this.setState({ displayed_form: '', current_wd: war, items: items, items_str: items_str, ofs: false });
          this.all_cats();
        });
    }
    else {
      this.setState({ displayed_form: '', current_wd: war, items: [], items_str: [] });
      this.all_cats();
      this.all_items();
    }
  };

  // TODO: Implement in actions
  open_cats = (e) => {
    e.preventDefault();
    var cat = e.target.id;
    var war = this.state.current_wd;
    let data = { wardrobe: war, category: cat };
    var items = [];
    var items_str = [];
    fetch(domain + '/get_items/', {
      method: 'POST',
      headers: {
        Authorization: `JWT ${localStorage.getItem('token')}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
      .then(res => res.json())
      .then(json => {
        //handle items here
        var url = '';
        for (var i = 0; i < json.length; i++) {
          if (json[i].image) {
            url = (json[i].image).substring(16);
          }
          items.push(<div key={i}><img src={url} name={json[i].name} width="100" height="100" alt="Item_photo" /><figcaption>{json[i].name}</figcaption></div>);
          items_str.push(json[i].name);
        }
        this.setState({ categories: [], displayed_form: '', current_wd: war, items: items, items_str: items_str });
        this.all_cats();
      });
  };

}

export default App;
<file_sep>from django.shortcuts import render, redirect
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.template.loader import get_template
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.models import User
from django.views.generic import View
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.files.storage import FileSystemStorage
from django.core.files.base import ContentFile
from django.conf import settings
from .forms import UserForm
from .serializers import *
from .models import Wardrobe, Outfit, OutfitItem

from rest_framework.response import Response
from rest_framework.decorators import api_view, permission_classes
from rest_framework.views import APIView
from rest_framework import status, permissions
from rest_framework.parsers import MultiPartParser, FormParser

import json
import os

# Create your views here.


@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def my_wardrobes(request):
    request.session.set_expiry(600)
    username = request.user.username
    wardrobes_list = Wardrobe.objects.filter(user=username)
    serializer = WardrobeSerializer(wardrobes_list, many=True)
    return Response(serializer.data)


@api_view(['GET', 'POST'])
@permission_classes([permissions.IsAuthenticated])
def my_cats(request):
    request.session.set_expiry(600)
    username = request.user.username
    curr_wd = request.data['curr_wd']
    items = Item.objects.filter(user=username, wardrobe=curr_wd)
    if (curr_wd == "Main Wardrobe"):
        items = Item.objects.filter(user=username)
    item_cats = ItemCategory.objects.filter(item_id__in=items.values('name'),
                                            user=username)
    categories_list = Category.objects.filter(
        category__in=item_cats.values('category_id'), user=username)
    serializer = CategorySerializer(categories_list, many=True)
    return Response(serializer.data)


@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def all_cats(request):
    request.session.set_expiry(600)
    username = request.user.username
    categories_list = Category.objects.filter(user=username)
    serializer = CategorySerializer(categories_list, many=True)
    return Response(serializer.data)


@api_view(['GET'])
def current_user(request):
    request.session.set_expiry(600)
    serializer = UserSerializer(request.user)
    if (serializer['username'].value is not ''):
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class add_wd(APIView):
    permission_classes = (permissions.IsAuthenticated, )

    def post(self, request, format=None):
        request.session.set_expiry(600)
        username = request.user.username
        wd_name = request.data['wardrobe']
        WD = Wardrobe(name=wd_name, user=username, image=request.data['image'])
        serializer = WardrobeSerializer(WD)
        WD.save()
        return Response(serializer.data,
                        status=status.HTTP_201_CREATED)


class add_cat(APIView):
    permission_classes = (permissions.IsAuthenticated, )

    def post(self, request, format=None):
        request.session.set_expiry(600)
        username = request.user.username
        cat_name = request.data['category']
        if (not Category.objects.filter(category=cat_name).exists()):
            category = Category(category=cat_name, user=username, image=request.data['image'])
            category.save()
        categories_list = Category.objects.filter(user=username)
        serializer = CategorySerializer(categories_list, many=True)
        return Response(serializer.data, status=status.HTTP_201_CREATED)


class add_item(APIView):
    permission_classes = (permissions.IsAuthenticated, )
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request, format=None):
        request.session.set_expiry(600)
        item_serializer = ItemSerializer(data=request.data)
        if (item_serializer.is_valid()):
            item_serializer.save()
            if (request.data['category']):
                it_category = ItemCategory(
                    category_id=request.data['category'],
                    item_id=request.data['name'],
                    user=request.user.username)
                it_category.save()
            return Response(item_serializer.data,
                            status=status.HTTP_201_CREATED)
        return Response(item_serializer.errors,
                        status=status.HTTP_400_BAD_REQUEST)


class add_outfit(APIView):
    permission_classes = (permissions.IsAuthenticated,)
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request, format=None):
        request.session.set_expiry(600)
        outfit = Outfit(name=request.data['name'], user=request.user.username, image=request.data['image'])
        for item in (request.data['items'].split(",")):
            out_item = OutfitItem(name=item, outfit=outfit.name, user=request.user.username)
            out_item.save()
        serializer = OutfitSerializer(outfit)
        outfit.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def get_items(request):
    request.session.set_expiry(600)
    wd = request.data['wardrobe']
    cat = request.data['category']
    items_list = []
    item_cats = ItemCategory.objects.filter(user=request.user.username)
    if wd == "Main Wardrobe":
        items_list = Item.objects.filter(user=request.user.username)
    else:
        items_list = Item.objects.filter(wardrobe=wd,
                                         user=request.user.username)
    if (cat == ''):
        items_list = items_list.exclude(name__in=item_cats.values('item_id'))
    else:
        item_cats = item_cats.filter(category_id=cat)
        items_list = items_list.filter(name__in=item_cats.values('item_id'))
    serializer = ItemSerializer(items_list, many=True)
    return Response(serializer.data)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def open_outfit(request):
    request.session.set_expiry(600)
    usern = request.user.username
    outfit_its = OutfitItem.objects.filter(outfit=request.data['outfit'], user=usern)
    items = Item.objects.filter(user=usern)
    items = items.filter(name__in=outfit_its.values('name'))
    serializer = ItemSerializer(items, many=True)
    return Response(serializer.data)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def item_outfits(request):
    print(request.data['item'])
    request.session.set_expiry(600)
    usern = request.user.username
    outfit_its = OutfitItem.objects.filter(name=request.data['item'], user=usern)
    outfits = Outfit.objects.filter(user=usern)
    outfits = outfits.filter(name__in=outfit_its.values('outfit'))
    serializer = ItemSerializer(outfits, many=True)
    return Response(serializer.data)

@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def outfits(request):
    request.session.set_expiry(600)
    usern = request.user.username
    outfits = Outfit.objects.filter(user=usern)
    serializer = OutfitSerializer(outfits, many=True)
    return Response(serializer.data)

@api_view(['GET'])
@permission_classes([permissions.IsAuthenticated])
def all_items(request):
    request.session.set_expiry(600)
    items_list = Item.objects.filter(user=request.user.username)
    serializer = ItemSerializer(items_list, many=True)
    return Response(serializer.data)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def del_item(request):
    request.session.set_expiry(600)
    item = request.data['name']
    item_obj = Item.objects.filter(user=request.user.username, name=item)
    serializer = ItemSerializer(item_obj)
    item_obj.delete()
    item_cat = ItemCategory.objects.filter(user=request.user.username, item_id=item)
    item_cat.delete()
    return Response(serializer.data, status=status.HTTP_201_CREATED)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def del_outfit(request):
    request.session.set_expiry(600)
    outfit = request.data['name']
    outfit_obj = Outfit.objects.filter(user=request.user.username, name=outfit)
    serializer = OutfitSerializer(outfit_obj)
    outfit_obj.delete()
    outfit_item = OutfitItem.objects.filter(user=request.user.username, outfit=outfit)
    outfit_item.delete()
    return Response(serializer.data, status=status.HTTP_201_CREATED)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def mod_item(request):
    request.session.set_expiry(600)
    item = request.data['name']
    item_obj = Item.objects.filter(user=request.user.username, name=item)
    item_cat = ItemCategory.objects.filter(user=request.user.username, item_id=item)
    if(request.data['new_name']):
        item_obj.update(name=request.data['new_name'])
        item_obj = Item.objects.filter(user=request.user.username, name=request.data['new_name'])
    if(request.data['wardrobe'] != ''):
        item_obj.update(wardrobe=request.data['wardrobe'])
    if(request.data['image']):
        file = request.data['image']
        media = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'images'))
        path = media.save(file.name, ContentFile(file.read()))
        item_obj.update(image=os.path.join('images', path))
    if(request.data['category']):
        if(request.data['new_name']):
            item = request.data['new_name']
        if(item_cat.exists()):
            item_cat.update(category_id=request.data['category'], item_id=item)
        else:
            cat_cr = ItemCategory(category_id=request.data['category'], item_id=item, user=request.user.username)
            cat_cr.save()
    return Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def mod_wd(request):
    current_wd = request.data['current_wd']
    user = request.user.username
    if(request.data['new_name']):
        wd = Wardrobe.objects.filter(name=current_wd, user=user)
        items = Item.objects.filter(wardrobe=current_wd, user=user)
        wd.update(name=request.data['new_name'])
        items.update(wardrobe=request.data['new_name'])
        return Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED)
    return Response(item_serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def del_wd(request):
    current_wd = request.data['current_wd']
    user = request.user.username
    wd = Wardrobe.objects.filter(name=current_wd, user=user)
    items = Item.objects.filter(wardrobe=current_wd, user=user)
    wd.delete()
    items.update(wardrobe='')
    return Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED)

@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def mod_cat(request):
    old_ct_ = request.data['category_old']
    user = request.user.username
    old_ct = Category.objects.filter(category=old_ct_, user=user)
    serializer = CategorySerializer(old_ct)
    if(old_ct.exists() and request.data['category_new']):
        items_ct = ItemCategory.objects.filter(category_id=old_ct_, user=user)
        old_ct.update(category=request.data['category_new'])
        items_ct.update(category_id=request.data['category_new'])
        return Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED)
    return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@permission_classes([permissions.IsAuthenticated])
def del_cat(request):
    old_ct_ = request.data['category_old']
    user = request.user.username
    old_ct = Category.objects.filter(category=old_ct_, user=user)
    serializer = CategorySerializer(old_ct)
    if(old_ct.exists()):
        items_ct = ItemCategory.objects.filter(user=user, category_id=old_ct_)
        old_ct.delete()
        items_ct.delete()
        return Response(UserSerializer(request.user).data, status=status.HTTP_201_CREATED)
    return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)

class UserList(APIView):
    permission_classes = (permissions.AllowAny, )

    def post(self, request, format=None):
        request.session.set_expiry(600)
        serializer = UserSerializerWithToken(data=request.data)
        if (serializer.is_valid()):
            serializer.save()
            Main_WD = Wardrobe(name="Main Wardrobe",
                               user=serializer['username'].value)
            Main_WD.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
<file_sep>import { combineReducers } from 'redux';
import auth from './auth';
import categories from './categories';
import items from './items';
import outfits from './outfits';
import wardrobes from './wardrobes';

const appReducer = combineReducers({
  auth, categories, items, outfits, wardrobes
})

const rootReducer = (state, action) => {
  if (action.type === 'USER_LOGOUT') {
    state = undefined;
  }
  return appReducer(state, action)
}

export default rootReducer;<file_sep>"""DigitalWardrobe URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from ClosetMap import views
from django.conf.urls import url
from rest_framework_jwt.views import obtain_jwt_token
from django.conf import settings
from django.views.static import serve

urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^my/', views.my_wardrobes),
    url(r'^add_item/', views.add_item.as_view()),
    url(r'^add_outfit/', views.add_outfit.as_view()),
    url(r'^item_outfits/', views.item_outfits),
    url(r'^get_items/', views.get_items),
    url(r'^outfits/', views.outfits),
    url(r'^open_outfit/', views.open_outfit),
    url(r'^del_outfit/', views.del_outfit),
    url(r'^all_items/', views.all_items),
    url(r'^del_item/', views.del_item),
    url(r'^mod_item/', views.mod_item),
    url(r'^del_wd/', views.del_wd),
    url(r'^mod_wd/', views.mod_wd),
    url(r'^del_cat/', views.del_cat),
    url(r'^mod_cat/', views.mod_cat),
    url(r'^add_wd/', views.add_wd.as_view()),
    url(r'^add_cat/', views.add_cat.as_view()),
    url(r'^my_cats/', views.my_cats),
    url(r'^all_cats/', views.all_cats),
    url(r'^current_user/', views.current_user),
    url(r'^users/', views.UserList.as_view()),
    url(r'^token-auth/', obtain_jwt_token),
    url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT,})
]
<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { categoryActions, itemActions, wardrobeActions } from '../actions';

/* Bootstrap */
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

// Confirm-alert
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';

var domain = 'http://closetmap.herokuapp.com';

class Wardrobe extends React.Component {
  constructor(props) {
    super(props);
    this.name = props.location.state.name;
    this.confirmDelete = this.confirmDelete.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
  }

  componentDidMount() {
    // Fetch categories in the wardrobe
    this.props.fetchWardrobeCategories(this.name)

    // Fetch items that are in the wardrobe
    if (this.name === 'Main Wardrobe') {
      this.props.fetchAllItems()
    } else {
      this.props.fetchWardrobeItems(this.name)
    }
  }

  confirmDelete() {
    confirmAlert({
      title: 'Confirm deletion',
      message: `Are you sure you want to delete wardrobe ${this.name}? \n All items will still be accessible through the main wardrobe.`,
      buttons: [
        {
          label: 'Yes, delete',
          onClick: () => this.handleDelete()
        },
        {
          label: 'Cancel'
        }
      ]
    })
  }

  handleDelete() {
    try {
      this.props.deleteWardrobe(this.name)
    } catch (err) {
      alert('An error occurred. Wardrobe not deleted.')
    }
  }

  renderLinks = () => {
    return (
      <div>
        <Link to={{
          pathname: '/edit-wrdb/',
          state: {
            wardrobeName: this.name,
            path: this.props.location.pathname,
          },
        }}>
          Rename wardrobe
        </Link>
        <button onClick={this.confirmDelete}>
          <FontAwesomeIcon icon="trash" />&nbsp;Delete wardrobe
        </button>
      </div>
    )
  }

  renderCategoryImage = (category) => {
    if (category.image) {
      return (
        <div className="square-image">
          {category.image &&
            <img alt={category.category} src={domain + category.image} />
          }
        </div>
      )
      // Render placeholder icon
    } else {
      return (
        <div className="category-default-img">
          <FontAwesomeIcon icon="folder" />
        </div>
      )
    }
  }

  // TODO:
  renderItemImage = (item) => {

  }

  render() {
    return (
      < div >
        {console.log('Wardrobe items', this.props.items)}
        {console.log('Wardrobe categories', this.props.categories)}
        <Link to="/home">Back to wardrobes</Link>
        {(this.name !== 'Main Wardrobe') && this.renderLinks()}
        <h1>{this.name}</h1>
        <p>Hint: scroll through your items,
          click on the buttons below to add an item or switch to the outfit view,
          you can view the details of your item by clicking on it.</p>
        <Container className="item-grid">
          <Row>
            {this.props.categories.map((category, index) => {
              return (
                <Col key={index} xs={4} md={3} lg={2}>
                  <Link to={{
                    pathname: `category/${category.category.replace(' ', '-')}`,
                    state: {
                      category: category,
                      wardrobe: this.name
                    }
                  }}
                    className="category-link"
                  >
                    <h2>{category.category}</h2>
                    {this.renderCategoryImage(category)}
                  </Link>
                </Col>
              )
            })}
            {this.props.items.filter((item) => {
              if (this.name !== 'Main Wardrobe') {
                return (item.wardrobe === this.name)
              } else {
                return true;
              }
            }).map((item, index) => {
              return (
                <Col key={item.pk} xs={4} md={3} lg={2}>
                  <Link to={{
                    pathname: `/items/${item.pk}`,
                    state: {
                      item: item
                    }
                  }}
                    className="item-link"
                  >
                    <h2>{item.name}</h2>
                    <div className="square-image">
                      {item.image &&
                        <img alt={item.name} src={domain + item.image} />
                      }
                    </div>
                  </Link>
                </Col>)
            })}
          </Row>
        </Container>
      </div >
    )
  }
}

const mapStateToProps = state => {
  console.log('STATE', state)
  return {
    // State has to be modified so that items are divided by wardrobe
    items: state.items.items,
    categories: state.categories.categories
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchWardrobeCategories: (wardrobeName) => {
      return dispatch(categoryActions.fetchWardrobeCategories(wardrobeName))
    },
    fetchWardrobeItems: (wardrobe) => {
      return dispatch(itemActions.fetchWardrobeItems(wardrobe))
    },
    fetchWardrobeCategoryItems: (wardrobe, category) => {
      return dispatch(itemActions.fetchWardrobeCategoryItems(wardrobe, category))
    },
    fetchAllItems: () => {
      return dispatch(itemActions.fetchAllItems())
    },
    deleteWardrobe: (wardrobeName) => {
      return dispatch(wardrobeActions.deleteWardrobe(wardrobeName))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Wardrobe);
<file_sep>from django.db import models

class Wardrobe(models.Model):
    name = models.CharField("Wardrobe name", max_length=255, default='')
    user = models.CharField("Owner", max_length=255, default='')
    image = models.ImageField(upload_to='images', blank=True, null=True)
    
    def __str__(self):
        return self.name

class Item(models.Model):
    name = models.CharField("Item name", max_length=255, default='')
    user = models.CharField("Owner", max_length=255, default='')
    wardrobe = models.CharField("Wardrobe", max_length=255, default='')
    image = models.ImageField(upload_to='images', blank=True, null=True)
    
    def __str__(self):
        return self.name

class ItemCategory(models.Model):
    category_id = models.CharField("Category", max_length=255, default='')
    item_id = models.CharField("Item", max_length=255, default='')
    user = models.CharField("User", max_length=255, null=True)

    def __str__(self):
        return self.category_id

class Category(models.Model):
    category = models.CharField("Category", max_length=255, default='')
    user = models.CharField("User", max_length=255, null=True)
    image = models.ImageField(upload_to='images', blank=True, null=True)

    def __str__(self):
        return self.category

class Outfit(models.Model):
    name = models.CharField("Outfit", max_length=255, default='')
    user = models.CharField("User", max_length=255, null=True)
    image = models.ImageField(upload_to='images', blank=True, null=True)

class OutfitItem(models.Model):
    name = models.CharField("Item", max_length=255, default='')
    outfit = models.CharField("Outfit", max_length=255, default='')
    user = models.CharField("User", max_length=255, null=True)

<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { categoryActions, itemActions } from '../actions';

/* Bootstrap */
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

// Confirm-alert
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';

var domain = 'http://closetmap.herokuapp.com';

class CategoryPage extends React.Component {
  constructor(props) {
    super(props)

    // Get data relayed through link from previous view
    this.category = props.location.state.category
    this.wardrobe = props.location.state.wardrobe

    this.confirmDelete = this.confirmDelete.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
  }

  componentDidMount() {
    this.props.fetchWardrobeCategoryItems(this.wardrobe, this.category.category);
  }

  // TODO:
  confirmDelete() {
    confirmAlert({
      title: 'Confirm deletion',
      message: `Are you sure you want to delete category ${this.category}? \n All items will remain in the wardrobe.`,
      buttons: [
        {
          label: 'Yes, delete',
          onClick: () => alert('Not implemented!')
        },
        {
          label: 'Cancel'
        }
      ]
    })
  }

  handleDelete() {
    try {
      // TODO: Delete category
      console.log('NOT IMPLEMENTED')
    } catch (err) {
      alert('An error occurred. Category not deleted.')
    }
  }

  renderItems() {
    return (
      <Row>
        {this.props.items.map((item) => {
          return (
            <Col key={item.pk} xs={4} md={3} lg={2}>
              <Link to={{
                pathname: `/items/${item.pk}`,
                state: {
                  item: item
                }
              }}
                className="item-link"
              >
                <h2>{item.name}</h2>
                <div className="square-image">

                  { // TODO: Render placeholder image!
                    item.image &&
                    <img alt={item.name} src={domain + item.image} />
                  }
                </div>
              </Link>
            </Col>)
        })
        }
      </Row>
    )
  }

  renderItemImage = () => {

  }

  /**
   * TODO:
   * - Add back link
   * - Add link to renaming category
   * - Add link for deleting category
   */

  render() {
    return (
      <div>
        <Link className="back-link" to="/home">Back to wardrobes</Link>
        <h1>{this.wardrobe}: {this.category.category}</h1>
        <Container className="item-grid">
          {this.renderItems()}
        </Container>
      </div>
    )
  }
}

const mapStateToProps = state => {
  return {
    items: state.items.items
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchWardrobeCategoryItems: (wardrobeName, categoryName) => {
      return dispatch(itemActions.fetchWardrobeCategoryItems(wardrobeName, categoryName))
    },
    deleteCategory: (e, data) => {
      return dispatch(categoryActions.deleteCategory(e, data))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(CategoryPage);
<file_sep>const express = require('express')
const proxy = require('http-proxy-middleware')
const path = require('path')

const app = express()
const PORT = process.env.PORT || 5000

// Proxy api request
// Optional, if you don't want your add CORS support to your Django app
app.use(
  '/api',
  proxy({
    target: "http://closetmap.herokuapp.com",
    changeOrigin: true,
    ws: true,
    pathRewrite: {
      '^/api': '',
    },
  })
)

app.use(express.static(path.join(__dirname, 'build')))

app.get('*', function(req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'))
})

app.listen(PORT, () => console.log(`Listening on :${PORT}`))
<file_sep>// Initial state for items
const initialState = {
  items: []
};

export default function items(state = initialState, action) {
  switch (action.type) {
    case 'FETCH_ALL_ITEMS':
    case 'FETCH_ITEMS_CATEGORY':
      return {
        ...state,
        items: [...action.items]
      }
    case 'FETCH_ITEMS':
      let filteredItems = action.items.filter((fetchedItem) => {
        return (state.items.findIndex(stateItem => {
          return stateItem.pk === fetchedItem.pk;
        }) === -1)
      })
      return {
        ...state,
        items: [...state.items, ...filteredItems]
      }
    case 'MODIFY_ITEM':
      let unmodifiedItems = state.items.filter((item) => {
        return (item.pk !== action.item)
      })
      return {
        ...state,
        items: [...unmodifiedItems, action.item]
      }
    // TODO: Update so it considers categories
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.item]
      }
    /* case 'DELETE_ITEM':
      return {
        ...state,
        items: [state.items.filter(item => item.pk !== action.item)]
      } */
    default:
      return state;
  }
}<file_sep>var domain = 'http://closetmap.herokuapp.com';

/**
 * Methods:
 * - login
 * - logout
 * - loadUser
 * - signup
 */

export const login = (username, password) => {
  return (dispatch, getState) => {
    return fetch(domain + '/token-auth/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ username, password })
    })
      .then(res => {
        if (res.status < 500) {
          return res.json().then(data => {
            return { status: res.status, data };
          })
        } else {
          console.log("Server error");
          throw res;
        }
      })
      .then(res => {
        if (res.status === 200) {
          dispatch({ type: 'LOGIN_SUCCESSFUL', data: res.data });
          return res.data;
        } else if (res.status === 403 || res.status === 401) {
          dispatch({ type: 'AUTHENTICATION_ERROR', data: res.data })
          // TODO: Dipslay error message to the user
          throw res.data;
        } else {
          dispatch({ type: 'LOGIN_FAILED', data: res.data });
          // TODO: Dipslay error message to the user
          throw res.data;
        }
      })
  }
}

// TODO
export const logout = () => {
  return (dispatch, getState) => {
    dispatch({ type: 'USER_LOGOUT', data: { errors: [], user: null } })
  }
}

export const loadUser = () => {
  return (dispatch, getState) => {
    dispatch({ type: 'USER_LOADING' });
    return fetch(domain + '/current_user/',
      {
        headers: {
          "Content-Type": "application/json",
          Authorization: `JWT ${getState().auth.token}`
        }
      })
      .then(res => {
        if (res.status < 500) {
          return res.json().then(data => {
            return { status: res.status, data };
          })
        } else {
          console.log("Server error");
          throw res;
        }
      })
      .then(res => {
        if (res.status === 200) {
          dispatch({ type: 'USER_LOADED', user: res.data });
          return res.data;
        } else if (res.status >= 400 && res.status < 500) {
          dispatch({ type: 'AUTHENTICATION_ERROR', data: res.data });
          throw res.data;
        }
      })
  }
}

export const signup = (username, password) => {
  return (dispatch, getState) => {
    return fetch(domain + '/users/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ username, password })
    })
      .then(res => {
        console.log('RES', res)
        if (res.status < 500) {
          return res.json().then(data => {
            return { status: res.status, data: data };
          })
        } else {
          console.log("Server error");
          throw res;
        }
      })
      .then(result => {
        if (result.status === 200 || result.status === 201) {
          dispatch({ type: 'REGISTRATION_SUCCESSFUL', data: result.data });
          return result.data;
        } else if (result.status === 403 || result.status === 401) {
          dispatch({ type: 'AUTHENTICATION_ERROR', data: result.data });
          // TODO: Display error message to the user
          throw result.data;
        } else {
          dispatch({ type: 'REGISTRATION_FAILED', data: result.data });
          // TODO: Display error message to the user
          throw result.data;
        }
      })
  }
}
<file_sep>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { wardrobeActions } from '../actions';
import { Link } from 'react-router-dom';

class WardrobeRenameForm extends Component {
  constructor(props) {
    super(props)

    this.wardrobeName = props.location.state.wardrobeName;
    this.wrdbPath = props.location.state.path;

    this.state = {
      new_name: '',
    };
  }

  handleChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    })
  };

  onSubmit = e => {
    e.preventDefault()
    // e => this.props.wd_modify(e, this.state)
    try {
      this.props.modifyWardrobe(this.wardrobeName, this.state.new_name)
      this.props.history.push(this.wrdbPath, { name: this.state.new_name })
    } catch (err) {
      console.log('Error', err)
    }
  }

  render() {
    return (
      <div className="WardrobeDelForm">
        <Link to="/home">Back</Link>
        <form onSubmit={this.onSubmit} className="WardrobeDelForm">
          <h4>Rename wardrobe '{this.wardrobeName}'</h4>
          <div>
            <label htmlFor="New_name">New name: </label>
            <input type="text" name='New_name' id='new_name' value={this.state.new_name} onChange={this.handleChange} />
          </div>
          <p>
            <input type="submit" value='Modify Wardrobe' />
          </p>
        </form>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    wardrobes: state.wardrobes.wardrobes,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    deleteWardrobe: (wardrobeName) => {
      dispatch(wardrobeActions.deleteWardrobe(wardrobeName))
    },
    modifyWardrobe: (wardrobeName, newName) => {
      dispatch(wardrobeActions.modifyWardrobe(wardrobeName, newName))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(WardrobeRenameForm);<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { Redirect, Link, withRouter } from 'react-router-dom';
import { authActions } from '../actions';

import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

import LoggedOutNav from './LoggedOutNav';

class LoginForm extends React.Component {
  state = {
    username: '',
    password: ''
  };

  onSubmit = e => {
    e.preventDefault();
    this.props.login(this.state.username, this.state.password)
  }

  handle_change = e => {
    const name = e.target.name;
    const value = e.target.value;
    this.setState(prevstate => {
      const newState = { ...prevstate };
      newState[name] = value;
      return newState;
    });
  };

  render() {
    if (this.props.isAuthenticated) {
      return <Redirect to="/home" />
    }
    return (
      <div>
        <LoggedOutNav />
        <Container>
          <Row>
            <Col >
              <form onSubmit={this.onSubmit} className="login-form" >
                <h1>Log In</h1>
                <label htmlFor="username">Username</label>
                <input
                  type="text"
                  name="username"
                  value={this.state.username}
                  onChange={this.handle_change}
                />
                <label htmlFor="password">Password</label>
                <input
                  type="<PASSWORD>"
                  name="password"
                  value={this.state.password}
                  onChange={this.handle_change}
                />
                <input type="submit" value="Login" />
                <p>Don't have an account yet?
                  <br />
                  <Link to='/signup'>Create an account!</Link>
                </p>
              </form>
              <div>
                {this.props.errors.length > 0 && (
                  <ul className="errors">
                    {this.props.errors.map(error => (
                      <li key={error.field}>{error.message}</li>
                    ))}
                  </ul>
                )}
              </div>
            </Col>
          </Row>
        </Container>
      </div>
    );
  }
}

const mapStateToProps = state => {
  let errors = [];
  if (state.auth.errors) {
    errors = Object.keys(state.auth.errors).map(field => {
      return { field, message: state.auth.errors[field] };
    });
  }
  return {
    errors,
    isAuthenticated: state.auth.isAuthenticated
  };
}

const mapDispatchToProps = dispatch => {
  return {
    login: (username, password) => {
      return dispatch(authActions.login(username, password));
    }
  };
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(LoginForm));<file_sep>from django.apps import AppConfig


class ClosetmapConfig(AppConfig):
    name = 'ClosetMap'
<file_sep>from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from .models import Wardrobe, Item, Category, ItemCategory, Outfit
from django.contrib.auth.models import User

class OutfitSerializer(serializers.ModelSerializer):
    class Meta:
        model = Outfit
        fields = ('pk', 'name', 'user', 'image')

class WardrobeSerializer(serializers.ModelSerializer):
    class Meta:
        model = Wardrobe
        fields = ('pk','name', 'user', 'image')

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = Item
        fields = ('pk', 'name', 'user', 'wardrobe', 'image',)

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('username',)

class ItemCategorySerializer(serializers.ModelSerializer):
    
    class Meta:
        model = ItemCategory
        fields = ('category_id', 'item_id', 'user',)
        

class CategorySerializer(serializers.ModelSerializer):
    
    class Meta:
        model = Category
        fields = ('category', 'user', 'image')

class UserSerializerWithToken(serializers.ModelSerializer):

    token = serializers.SerializerMethodField()
    password = serializers.CharField(write_only=True)

    def get_token(self, obj):
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

        payload = jwt_payload_handler(obj)
        token = jwt_encode_handler(payload)
        return token

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    class Meta:
        model = User
        fields = ('token', 'username', 'password',)
<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { outfitActions } from '../actions';

/* Bootstrap */
import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

var domain = 'http://closetmap.herokuapp.com';

class Outfits extends React.Component {

  componentDidMount() {
    this.props.fetchOutfits()
  }

  renderImage = (outfit) => {
    if (outfit.image) {
      return (
        <div className="square-image">
          {outfit.image &&
            <img alt={outfit.name} src={domain + outfit.image} />
          }
        </div>
      )
    } else {
      return (
        <div className="outfit-default-img">
          <FontAwesomeIcon icon="walking" />
        </div>
      )
    }
  }

  render() {
    return (
      <Container>
        <Link to="/home">Back</Link>
        <h1>Your Outfits</h1>
        <p>Hint: scroll through your outfits. Click on the buttons below to add an
          outfit or switch to the items view. You can view the details of your outfit by clicking on it.</p>
        <Row>
          {this.props.outfits.map((outfit, index) => {
            return (
              <Col key={outfit.pk} xs={4} md={3} lg={2}>
                <Link to={{
                  pathname: `/outfit/${outfit.pk}`,
                  state: {
                    outfit: outfit
                  }
                }}
                  className="outfit-link"
                >
                  <h2>{outfit.name}</h2>
                  {this.renderImage(outfit)}
                </Link>
              </Col>)
          })}
        </Row>
      </Container>
    )
  }
}

const mapStateToProps = state => {
  return {
    outfits: state.outfits.outfits
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchOutfits: () => {
      dispatch(outfitActions.fetchOutfits())
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(Outfits);
<file_sep>djangorestframework
django-cors-headers
djangorestframework-jwt
Pillow
certifi==2018.11.29
chardet==3.0.4
defusedxml==0.5.0
dj-database-url==0.5.0
Django==2.1.3
django-bootstrap3==11.0.0
django-heroku==0.3.1
gunicorn==19.9.0
idna==2.8
oauthlib==3.0.1
psycopg2==2.7.7
psycopg2-binary==2.7.7
PyJWT==1.7.1
python-social-auth==0.3.6
python3-openid==3.1.0
pytz==2018.7
requests==2.21.0
requests-oauthlib==1.2.0
six==1.12.0
social-auth-app-django==3.1.0
social-auth-core==3.1.0
urllib3==1.24.1
whitenoise==4.1.2
<file_sep>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { itemActions, outfitActions } from '../actions';
import { Link } from 'react-router-dom';

class OutfitForm extends Component {
  constructor(props) {
    super(props)

    this.state = {
      name: '',
      items: [],
      item: '',
      image: '',
    };
  }

  componentDidMount() {
    this.props.fetchAllItems();
  }

  onSubmit = e => {
    e.preventDefault();
    this.props.addOutfit(this.state)
  }

  handleChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    })
  };

  handleImageChange = (e) => {
    this.setState({
      image: e.target.files[0]
    })
  };

  handle_add = (e) => {
    e.preventDefault();
    if (this.state.item === 'None' || this.state.item === '') {
      return;
    }
    this.state.items.push(this.state.item);
    this.setState({ item: '' });
    console.log(this.state.items);
  };

  // Todo: write

  render() {
    return (
      <div className="OutfitForm">
        <form onSubmit={this.onSubmit} className="OutfitForm">
          <h4>Create outfit</h4>
          <Link to="/home">Back</Link>
          <p>Hint: you are in the shoes of a fashion designer: what do you want to wear? Add item to combine them in your dream outfit and name your creation!
</p>
          <div>
            <label htmlFor="Item">Add item: </label>
            <select name='Item' id='item' value={this.state.item} onChange={this.handleChange} >
              <option key={'none'} value={null}>None</option>
              {this.props.items.map((item, index) => {
                return (<option key={index} value={item.name}>{item.name}</option>);
              })}
            </select>
          </div>
          <div>Added items:</div>
          <ul>
            {this.state.items.map(id => (<Row id={id} />))}
          </ul>
          <button onClick={this.handle_add}>Add Item</button>
          <div>
            <label htmlFor="name">Outfit name: </label>
            <input type="text" name='name' id='name' value={this.state.name} onChange={this.handleChange} required />
          </div>
          <div>
            <label htmlFor="photo">Add photo</label>
            <input type="file"
              name="photo"
              id="image"
              accept="image/png, image/jpeg" onChange={this.handleImageChange} />
          </div>
          <p>

            <input type="submit" value='Create outfit' />
          </p>
        </form>
      </div>
    );
  }
}

const Row = ({ id }) => (
  <li key={id}>{id}</li>
);

const mapStateToProps = state => {
  return {
    items: state.items.items,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchAllItems: () => {
      dispatch(itemActions.fetchAllItems())
    },
    addOutfit: (data) => {
      dispatch(outfitActions.addOutfit(data))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(OutfitForm);<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { wardrobeActions } from '../actions';
import { Link } from 'react-router-dom';

class WardrobeForm extends React.Component {
  state = {
    wardrobe: '',
    image: null
  };

  onSubmit = e => {
    e.preventDefault();
    this.props.addWardrobe(this.state)
  }

  handle_change = e => {
    const name = e.target.name;
    const value = e.target.value;
    this.setState(prevstate => {
      const newState = { ...prevstate };
      newState[name] = value;
      return newState;
    });
  };

  handleImageChange = (e) => {
    this.setState({
      image: e.target.files[0]
    })
  };

  render() {
    return (
      <form onSubmit={this.onSubmit}>
        <Link to="/home">Back</Link>
        <h4>Add Wardrobe</h4>
        <label htmlFor="wardrobe">Wardrobe name</label>
        <input
          type="text"
          name="wardrobe"
          value={this.state.wardrobe}
          onChange={this.handle_change}
        />
        <label htmlFor="photo">Set icon</label>
        <input type="file"
          name="photo"
          id="image"
          accept="image/png, image/jpeg" onChange={this.handleImageChange} />
        <input type="submit" value="Add Wardrobe" />
      </form>
    );
  }
}

const mapStateToProps = state => {
  return {
  }
}

const mapDispatchToProps = dispatch => {
  return {
    addWardrobe: (data) => {
      return dispatch(wardrobeActions.addWardrobe(data))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(WardrobeForm);
<file_sep>import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { outfitActions, itemActions } from '../actions';

import { Container, Row, Col, Table, } from 'react-bootstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

// Confirm-alert
import { confirmAlert } from 'react-confirm-alert';
import 'react-confirm-alert/src/react-confirm-alert.css';

var domain = 'http://closetmap.herokuapp.com';

class ItemPage extends Component {
  constructor(props) {
    super(props)
    this.item = props.location.state.item;
    this.confirmDelete = this.confirmDelete.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
    this.goBack = this.goBack.bind(this);
  }

  componentDidMount() {
    this.props.outfitsByItem(this.item)
  }

  goBack() {
    this.props.history.goBack();
  }

  confirmDelete() {
    confirmAlert({
      title: 'Confirm deletion',
      message: `Are you sure you want to delete ${this.item.name}?`,
      buttons: [
        {
          label: 'Yes, delete',
          onClick: () => this.handleDelete()
        },
        {
          label: 'Cancel'
        }
      ]
    })
  }

  handleDelete() {
    try {
      this.props.deleteItem(this.item)
    } catch (err) {
      alert('An error occurred. Item not deleted.')
    }
  }

  renderImage() {
    console.log('Image', this.item.image)
    if (this.item.image) {
      return (
        <div className="square-image">
          <img className="itempage-img" alt={this.item.name} src={domain + this.item.image} />
        </div>)
    } else {
      return (
        <div className="item-placeholder-img">
          <FontAwesomeIcon icon="tshirt" />
        </div>
      )
    }
  }

  renderOutfitImage(outfit) {
    if (outfit.image) {
      return (
        <div className="square-image">
          {outfit.image &&
            <img alt={outfit.name} src={domain + outfit.image} />
          }
        </div>
      )
    } else {
      return (
        <div className="outfit-default-img">
          <FontAwesomeIcon icon="walking" />
        </div>
      )
    }
  }

  renderOutfits() {
    if (this.props.itemOutfits.length > 0) {
      return (
        <Row>
          {this.props.itemOutfits.map((outfit) => {
            return (
              <Col key={outfit.pk} xs={4} md={3} lg={2}>
                <Link to={{
                  pathname: `/outfit/${outfit.pk}`,
                  state: {
                    outfit: outfit
                  }
                }}
                  className="item-link"
                >
                  <h3>{outfit.name}</h3>
                  {this.renderOutfitImage(outfit)}
                </Link>

              </Col>
            )
          })}
        </Row>
      )
    } else {
      return (
        <Row>
          <Col>
            <p>This item is not yet part of any outfit</p>
          </Col>
        </Row>

      )
    }
  }

  render() {
    return (
      <div className="page-container">
        <button className="back-button" onClick={this.goBack}>Back</button>

        <Link to={{
          pathname: '/del-item/',
          state: this.item,
        }}>
          Edit item
        </Link>
        <button onClick={this.confirmDelete}>
          <FontAwesomeIcon icon="trash" />&nbsp;Delete item
        </button>
        <Container>
          <Row>
            <Col xs={12} md={6}>
              {this.renderImage()}
            </Col>
            <Col xs={12} md={6}>
              <h1 className="itempage-title">{this.item.name}</h1>
              <Table responsive>
                <tbody>
                  <tr>
                    <td>Wardrobe</td>
                    <td>{this.item.wardrobe}</td>
                  </tr>
                  <tr>
                    <td>Category</td>
                    <td>{this.item.category ? this.itemcategory : '--'}</td>
                  </tr>
                  <tr>
                    <td>Season</td>
                    <td><em>Coming in future version</em></td>
                  </tr>
                  <tr>
                    <td>Tags</td>
                    <td><em>Coming in future version</em></td>
                  </tr>
                </tbody>
              </Table>
            </Col>
          </Row>
          <h2>Outfits</h2>
           {this.renderOutfits()}
        </Container>
      </div >
    )
  }
}

const mapStateToProps = state => {
  return {
    itemOutfits: state.outfits.itemOutfits
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchOutfits: () => {
      return dispatch(outfitActions.fetchOutfits())
    },
    outfitsByItem: (item) => {
      return dispatch(outfitActions.outfitsByItem(item))
    },
    deleteItem: (item) => {
      dispatch(itemActions.deleteItem(item))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ItemPage);
<file_sep>from django.contrib import admin
from .models import Item
from .models import Outfit
# Register your models here.

admin.site.register(Item)
admin.site.register(Outfit)
<file_sep>import React from 'react';
import { Link } from 'react-router-dom';

import Container from 'react-bootstrap/Container';
import LoggedOutNav from './LoggedOutNav';

class FAQ extends React.Component {
    render() {
        return (
            <div>
                <LoggedOutNav />
                <Container>
                    <Link to="/">Back</Link>
                    <h1>Frequently asked questions</h1>
                    <p> * </p>
                    <h2>Who are you?</h2>
                    <p>We are students in Computer Science at Aalto University in Finland. We have designed this website as part of
            our study program. </p>
                    <h2>Is the website for me?</h2>
                    <p>
                        The website is mainly for people who store their clothes in more than one place and do not know where they
                        are. The main features of our app are creating different wardrobes with different categories and adding
                        items and outfits and their respective picture, all DIY.
        </p>
                    <p>
                        If you are a living with divorced parents, in a student house, if you have clothes at your partner’s house,
            or if you simply want to be organized and carry your closet in your pocket, that website is for you.</p>
                    <h2>What is the difference between adding an item and adding an outfit?
        </h2>
                    <p>An outfit consists of several items. You then first need to have items to create an outfit.</p>


                    <h2>How to move items ?</h2>
                    <p>When you open the wardrobe and click on an item and reach the editing page. There, you can change its
            location and/or category. </p>
                    <h2>What happens if I delete a category?</h2>
                    <p> Only the category is deleted. The items are still there but do not belong to the category any more.</p>
                    <h2>What do you do with my data?</h2>
                    <p>Your data are personal and we make no use of them. </p>
                    <h2>Can I move an entire outfit form one wardrobe to another at once?</h2>
                    <p> No but it will be implemented in the next update.</p>
                    <h2>What shall be added in the future updates?</h2>
                    <p> In the next update,we will add the possibility to
            <ol>
                            <li>Move an entire outfit from one wardrobe to another in one click.</li>
                            <li>View all the clothes with or without the categories.</li>
                            <li>Choose different icons for the wardrobe and the items.</li>
                        </ol>
                    </p>

                    <p> * </p>
                    <p>* * </p>
                    <p> * </p>
                    <p>Contact us by email on <EMAIL></p>
                </Container>
            </div>
        )
    }
}
export default FAQ;<file_sep>import React from 'react';
import { connect } from 'react-redux';
import { categoryActions } from '../actions';
import { Link } from 'react-router-dom';

class CategoryForm extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      category: '',
      category_old: '',
      category_new: '',
      current_wd: this.props.current_wd,
      image: ''
    };
  }

  componentDidMount() {
    this.props.fetchAllCategories()
  }

  onSubmit = (e) => {
    e.preventDefault();
    this.props.addCategory(this.state)
  }

  handleChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    })
  };

  handleImageChange = (e) => {
    this.setState({
      image: e.target.files[0]
    })
  };

  render() {
    return (
      <form onSubmit={this.onSubmit}>
        <h4>Manage Categories</h4>
        <Link to="/home">Back</Link>
        <div>
          <label htmlFor="Category">Category: </label>
          <select name='Category' id='category_old' value={this.state.category_old} onChange={this.handleChange} >
            <option key={'none'} value={null}>None</option>
            {this.props.categories.map((cat, index) => {
              return (<option key={index} value={cat.category}>{cat.category}</option>);
            })}
          </select>
        </div>
        <div>
          <label htmlFor="New_name">New name: </label>
          <input type="text" name='New_name' id='category_new' value={this.state.category_new} onChange={this.handleChange} />
        </div>
        <p>
          <button onClick={e => this.props.deleteCategory(e, this.state)}>Delete Category</button>
          <button onClick={e => this.props.modifyCategory(e, this.state)}>Modify Category</button>
        </p>
        <label htmlFor="category">Add Category: </label>
        <input
          type="text"
          name="category"
          id="category"
          value={this.state.category}
          onChange={this.handleChange}
        />
        <label htmlFor="photo">Upload category photo</label>
        <input type="file"
          name="photo"
          id="image"
          accept="image/png, image/jpeg" onChange={this.handleImageChange} />
        <input type="submit" value="New Category" />
      </form>
    );
  }
}

const mapStateToProps = state => {
  return {
    wardrobes: state.wardrobes,
    categories: state.categories.categories,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    addCategory: (data) => {
      return dispatch(categoryActions.addCategory(data));
    },
    fetchWardrobeCategories: (wardrobe) => {
      return dispatch(categoryActions.fetchWardrobeCategories(wardrobe));
    },
    fetchAllCategories: () => {
      return dispatch(categoryActions.fetchAllCategories());
    },
    deleteCategory: (e, data) => {
      return dispatch(categoryActions.deleteCategory(e, data));
    },
    modifyCategory: (e, data) => {
      return dispatch(categoryActions.modifyCategory(e, data));
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(CategoryForm);
