Put logout, settings under dropdown menu with logged in user's profile picture

- Create dropdown menu. Put settings page, logout action under it
- Make user's profile picture the dropdown menu heading
- Create khoj.js to store shared js across web client
  It currently stores the dropdown menu open, close functionality
- Put shared styling for khoj dropdown menu under khoj.css
This commit is contained in:
Debanjum Singh Solanky 2023-11-01 01:59:36 -07:00
parent 58a7171911
commit f585a71744
7 changed files with 163 additions and 18 deletions

View file

@ -100,6 +100,65 @@ p#khoj-banner {
display: inline;
}
/* Dropdown in navigation menu*/
.khoj-nav-dropdown-content {
display: block;
grid-auto-flow: row;
position: absolute;
background-color: var(--background-color);
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
right: 15vw;
z-index: 1;
opacity: 0;
transition: opacity 0.1s ease-in-out;
pointer-events: none;
text-align: left;
}
.khoj-nav-dropdown-content.show {
opacity: 1;
pointer-events: auto;
}
.khoj-nav-dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.khoj-nav-dropdown-content a:hover {
background-color: var(--primary-hover);
}
.khoj-nav-username {
padding: 12px 16px;
text-decoration: none;
display: block;
font-weight: bold;
}
img.circle {
border-radius: 50%;
border: 2px solid var(--primary-hover);
width: 40px;
height: 40px;
vertical-align: text-top;
}
@media screen and (max-width: 700px) {
.khoj-nav-dropdown-content {
display: block;
grid-auto-flow: row;
position: absolute;
background-color: var(--background-color);
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
right: 10px;
z-index: 1;
opacity: 0;
transition: opacity 0.1s ease-in-out;
pointer-events: none;
}
}
@media only screen and (max-width: 600px) {
div.khoj-header {
display: grid;

View file

@ -0,0 +1,15 @@
// Toggle the navigation menu
function toggleMenu() {
var menu = document.getElementById("khoj-nav-menu");
menu.classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
document.addEventListener('click', function(event) {
let menu = document.getElementById("khoj-nav-menu");
let menuContainer = document.getElementById("khoj-nav-menu-container");
let isClickOnMenu = menuContainer.contains(event.target) || menuContainer === event.target;
if (isClickOnMenu === false && menu.classList.contains("show")) {
menu.classList.remove("show");
}
});

View file

@ -8,6 +8,7 @@
<link rel="stylesheet" href="/static/assets/pico.min.css">
<link rel="stylesheet" href="/static/assets/khoj.css">
</head>
<script type="text/javascript" src="/static/assets/khoj.js"></script>
<body class="khoj-configure">
<div class="khoj-header-wrapper">
<div class="filler"></div>
@ -18,7 +19,15 @@
<nav class="khoj-nav">
<a class="khoj-nav" href="/chat">💬 Chat</a>
<a class="khoj-nav" href="/">🔎 Search</a>
<a class="khoj-nav khoj-nav-selected" href="/config">⚙️ Settings</a>
<!-- Dropdown Menu -->
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
<img class="circle" src="{{ user_photo }}" alt="{{ username }}" onclick="toggleMenu()">
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
<div class="khoj-nav-username"> {{ username }} </div>
<a class="khoj-nav khoj-nav-selected" href="/config">⚙️ Settings</a>
<a class="khoj-nav" href="/auth/logout">🔑 Logout</a>
</div>
</div>
</nav>
</div>
<div class="filler"></div>

View file

@ -8,6 +8,7 @@
<link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="stylesheet" href="/static/assets/khoj.css">
</head>
<script type="text/javascript" src="/static/assets/khoj.js"></script>
<script>
let chatOptions = [];
function copyProgrammaticOutput(event) {
@ -284,7 +285,14 @@
<a class="khoj-nav khoj-nav-selected" href="/chat">💬 Chat</a>
<a class="khoj-nav" href="/">🔎 Search</a>
{% if not demo %}
<a class="khoj-nav" href="/config">⚙️ Settings</a>
<!-- Dropdown Menu -->
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
<img class="circle" src="{{ user_photo }}" alt="{{ username }}" onclick="toggleMenu()">
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
<div class="khoj-nav-username"> {{ username }} </div>
<a class="khoj-nav khoj-nav-selected" href="/config">⚙️ Settings</a>
<a class="khoj-nav" href="/auth/logout">🔑 Logout</a>
</div>
{% endif %}
</nav>
</div>

View file

@ -3,11 +3,6 @@
<div class="page">
<div class="section">
{% if anonymous_mode == False %}
<div>
Logged in as {{ username }}
</div>
{% endif %}
<h2 class="section-title">Plugins</h2>
<div class="section-cards">
<div class="card">
@ -328,11 +323,6 @@
<div class="finalize-buttons">
<button id="reinitialize" type="submit" title="Regenerate index from scratch">🔄 Reinitialize</button>
</div>
{% if anonymous_mode == False %}
<div class="finalize-buttons">
<button id="logout" class="logout" onclick="window.location.href='/auth/logout'">Logout</button>
</div>
{% endif %}
</div>
</div>
</div>

View file

@ -10,6 +10,7 @@
</head>
<script type="text/javascript" src="/static/assets/org.min.js"></script>
<script type="text/javascript" src="/static/assets/markdown-it.min.js"></script>
<script type="text/javascript" src="/static/assets/khoj.js"></script>
<script>
function render_image(item) {
@ -296,7 +297,15 @@
<a class="khoj-nav" href="/chat">💬 Chat</a>
<a class="khoj-nav khoj-nav-selected" href="/">🔎 Search</a>
{% if not demo %}
<a class="khoj-nav" href="/config">⚙️ Settings</a>
<!-- Dropdown Menu -->
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
<img class="circle" src="{{ user_photo }}" alt="{{ username }}" onclick="toggleMenu()">
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
<div class="khoj-nav-username"> {{ username }} </div>
<a class="khoj-nav" href="/config">⚙️ Settings</a>
<a class="khoj-nav" href="/auth/logout">🔑 Logout</a>
</div>
</div>
{% endif %}
</nav>
</div>

View file

@ -34,19 +34,52 @@ VALID_TEXT_CONTENT_TYPES = ["org", "markdown", "pdf", "plaintext"]
@web_client.get("/", response_class=FileResponse)
@requires(["authenticated"], redirect="login_page")
def index(request: Request):
return templates.TemplateResponse("index.html", context={"request": request, "demo": state.demo})
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
return templates.TemplateResponse(
"index.html",
context={
"request": request,
"demo": state.demo,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@web_client.post("/", response_class=FileResponse)
@requires(["authenticated"], redirect="login_page")
def index_post(request: Request):
return templates.TemplateResponse("index.html", context={"request": request, "demo": state.demo})
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
return templates.TemplateResponse(
"index.html",
context={
"request": request,
"demo": state.demo,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@web_client.get("/chat", response_class=FileResponse)
@requires(["authenticated"], redirect="login_page")
def chat_page(request: Request):
return templates.TemplateResponse("chat.html", context={"request": request, "demo": state.demo})
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
return templates.TemplateResponse(
"chat.html",
context={
"request": request,
"demo": state.demo,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@web_client.get("/login", response_class=FileResponse)
@ -84,6 +117,7 @@ if not state.demo:
@requires(["authenticated"], redirect="login_page")
def config_page(request: Request):
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
enabled_content = set(EntryAdapters.get_unique_file_types(user).all())
default_full_config = FullConfig(
content_type=None,
@ -129,6 +163,7 @@ if not state.demo:
"current_model_state": successfully_configured,
"anonymous_mode": state.anonymous_mode,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@ -136,6 +171,7 @@ if not state.demo:
@requires(["authenticated"], redirect="login_page")
def github_config_page(request: Request):
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
current_github_config = get_user_github_config(user)
if current_github_config:
@ -158,13 +194,20 @@ if not state.demo:
current_config = {} # type: ignore
return templates.TemplateResponse(
"content_type_github_input.html", context={"request": request, "current_config": current_config}
"content_type_github_input.html",
context={
"request": request,
"current_config": current_config,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@web_client.get("/config/content_type/notion", response_class=HTMLResponse)
@requires(["authenticated"], redirect="login_page")
def notion_config_page(request: Request):
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
current_notion_config = get_user_notion_config(user)
current_config = NotionContentConfig(
@ -174,7 +217,13 @@ if not state.demo:
current_config = json.loads(current_config.json())
return templates.TemplateResponse(
"content_type_notion_input.html", context={"request": request, "current_config": current_config}
"content_type_notion_input.html",
context={
"request": request,
"current_config": current_config,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@web_client.get("/config/content_type/{content_type}", response_class=HTMLResponse)
@ -185,6 +234,7 @@ if not state.demo:
object = map_config_to_object(content_type)
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
config = object.objects.filter(user=user).first()
if config == None:
config = object.objects.create(user=user)
@ -202,6 +252,8 @@ if not state.demo:
"request": request,
"current_config": current_config,
"content_type": content_type,
"username": user.username if user else None,
"user_photo": user_picture,
},
)
@ -209,6 +261,7 @@ if not state.demo:
@requires(["authenticated"], redirect="login_page")
def conversation_processor_config_page(request: Request):
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
openai_config = ConversationAdapters.get_openai_conversation_config(user)
if openai_config:
@ -229,5 +282,7 @@ if not state.demo:
context={
"request": request,
"current_config": current_processor_openai_config,
"username": user.username if user else None,
"user_photo": user_picture,
},
)